1
1
from django .db import NotSupportedError
2
- from django .db .models import Expression , FloatField
2
+ from django .db .models import Expression , FloatField , JSONField
3
+ from django .db .models .expressions import F , Value
3
4
4
5
5
6
class Operator :
@@ -75,42 +76,57 @@ def __repr__(self):
75
76
def as_sql (self , compiler , connection ):
76
77
return "" , []
77
78
79
+ def _get_indexed_fields (self , mappings ):
80
+ for field , definition in mappings .get ("fields" , {}).items ():
81
+ yield field
82
+ for path in self ._get_indexed_fields (definition ):
83
+ yield f"{ field } .{ path } "
84
+
78
85
def _get_query_index (self , fields , compiler ):
79
86
fields = set (fields )
80
87
for search_indexes in compiler .collection .list_search_indexes ():
81
88
mappings = search_indexes ["latestDefinition" ]["mappings" ]
82
- if mappings ["dynamic" ] or fields .issubset (set (mappings ["fields" ])):
89
+ indexed_fields = set (self ._get_indexed_fields (mappings ))
90
+ if mappings ["dynamic" ] or fields .issubset (indexed_fields ):
83
91
return search_indexes ["name" ]
84
92
return "default"
85
93
86
- def search_operator (self ):
94
+ def search_operator (self , compiler , connection ):
87
95
raise NotImplementedError
88
96
89
97
def as_mql (self , compiler , connection ):
90
- index = self ._get_query_index (self .get_search_fields (), compiler )
91
- return {"$search" : {** self .search_operator (), "index" : index }}
98
+ index = self ._get_query_index (self .get_search_fields (compiler , connection ), compiler )
99
+ return {"$search" : {** self .search_operator (compiler , connection ), "index" : index }}
92
100
93
101
94
102
class SearchAutocomplete (SearchExpression ):
95
- def __init__ (self , path , query , fuzzy = None , score = None ):
96
- self .path = path
97
- self .query = query
103
+ def __init__ (self , path , query , fuzzy = None , token_order = None , score = None ):
104
+ self .path = F (path ) if isinstance (path , str ) else path
105
+ self .query = Value (query ) if not hasattr (query , "resolve_expression" ) else query
106
+ if fuzzy is not None and not hasattr (fuzzy , "resolve_expression" ):
107
+ fuzzy = Value (fuzzy , output_field = JSONField ())
98
108
self .fuzzy = fuzzy
109
+ if token_order is not None and not hasattr (token_order , "resolve_expression" ):
110
+ token_order = Value (token_order )
111
+ self .token_order = token_order
99
112
self .score = score
100
113
super ().__init__ ()
101
114
102
- def get_search_fields (self ):
103
- return {self .path }
115
+ def get_search_fields (self , compiler , connection ):
116
+ # Shall i implement resolve_something? I think I have to do
117
+ return {self .path .as_mql (compiler , connection , as_path = True )}
104
118
105
- def search_operator (self ):
119
+ def search_operator (self , compiler , connection ):
106
120
params = {
107
- "path" : self .path ,
108
- "query" : self .query ,
121
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
122
+ "query" : self .query . as_mql ( compiler , connection ) ,
109
123
}
110
124
if self .score is not None :
111
- params ["score" ] = self .score
125
+ params ["score" ] = self .score . as_mql ( compiler , connection )
112
126
if self .fuzzy is not None :
113
- params ["fuzzy" ] = self .fuzzy
127
+ params ["fuzzy" ] = self .fuzzy .as_mql (compiler , connection )
128
+ if self .token_order is not None :
129
+ params ["tokenOrder" ] = self .token_order .as_mql (compiler , connection )
114
130
return {"autocomplete" : params }
115
131
116
132
@@ -121,16 +137,16 @@ def __init__(self, path, value, score=None):
121
137
self .score = score
122
138
super ().__init__ ()
123
139
124
- def get_search_fields (self ):
125
- return {self .path }
140
+ def get_search_fields (self , compiler , connection ):
141
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
126
142
127
- def search_operator (self ):
143
+ def search_operator (self , compiler , connection ):
128
144
params = {
129
- "path" : self .path ,
130
- "value" : self .value ,
145
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
146
+ "value" : self .value . as_mql ( compiler , connection , as_path = True ) ,
131
147
}
132
148
if self .score is not None :
133
- params ["score" ] = self .score
149
+ params ["score" ] = self .score . as_mql ( compiler , connection , as_path = True )
134
150
return {"equals" : params }
135
151
136
152
@@ -140,15 +156,15 @@ def __init__(self, path, score=None):
140
156
self .score = score
141
157
super ().__init__ ()
142
158
143
- def get_search_fields (self ):
144
- return {self .path }
159
+ def get_search_fields (self , compiler , connection ):
160
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
145
161
146
- def search_operator (self ):
162
+ def search_operator (self , compiler , connection ):
147
163
params = {
148
- "path" : self .path ,
164
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
149
165
}
150
166
if self .score is not None :
151
- params ["score" ] = self .score
167
+ params ["score" ] = self .score . definitions
152
168
return {"exists" : params }
153
169
154
170
@@ -159,16 +175,16 @@ def __init__(self, path, value, score=None):
159
175
self .score = score
160
176
super ().__init__ ()
161
177
162
- def get_search_fields (self ):
163
- return {self .path }
178
+ def get_search_fields (self , compiler , connection ):
179
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
164
180
165
- def search_operator (self ):
181
+ def search_operator (self , compiler , connection ):
166
182
params = {
167
- "path" : self .path ,
168
- "value" : self .value ,
183
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
184
+ "value" : self .value . as_mql ( compiler , connection , as_path = True ) ,
169
185
}
170
186
if self .score is not None :
171
- params ["score" ] = self .score
187
+ params ["score" ] = self .score . definitions
172
188
return {"in" : params }
173
189
174
190
@@ -181,20 +197,20 @@ def __init__(self, path, query, slop=None, synonyms=None, score=None):
181
197
self .synonyms = synonyms
182
198
super ().__init__ ()
183
199
184
- def get_search_fields (self ):
185
- return {self .path }
200
+ def get_search_fields (self , compiler , connection ):
201
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
186
202
187
- def search_operator (self ):
203
+ def search_operator (self , compiler , connection ):
188
204
params = {
189
- "path" : self .path ,
190
- "query" : self .query ,
205
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
206
+ "query" : self .query . as_mql ( compiler , connection , as_path = True ) ,
191
207
}
192
208
if self .score is not None :
193
- params ["score" ] = self .score
209
+ params ["score" ] = self .score . as_mql ( compiler , connection , as_path = True )
194
210
if self .slop is not None :
195
- params ["slop" ] = self .slop
211
+ params ["slop" ] = self .slop . as_mql ( compiler , connection , as_path = True )
196
212
if self .synonyms is not None :
197
- params ["synonyms" ] = self .synonyms
213
+ params ["synonyms" ] = self .synonyms . as_mql ( compiler , connection , as_path = True )
198
214
return {"phrase" : params }
199
215
200
216
@@ -205,16 +221,16 @@ def __init__(self, path, query, score=None):
205
221
self .score = score
206
222
super ().__init__ ()
207
223
208
- def get_search_fields (self ):
209
- return {self .path }
224
+ def get_search_fields (self , compiler , connection ):
225
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
210
226
211
- def search_operator (self ):
227
+ def search_operator (self , compiler , connection ):
212
228
params = {
213
229
"defaultPath" : self .path ,
214
- "query" : self .query ,
230
+ "query" : self .query . as_mql ( compiler , connection , as_path = True ) ,
215
231
}
216
232
if self .score is not None :
217
- params ["score" ] = self .score
233
+ params ["score" ] = self .score . definitions
218
234
return {"queryString" : params }
219
235
220
236
@@ -228,15 +244,15 @@ def __init__(self, path, lt=None, lte=None, gt=None, gte=None, score=None):
228
244
self .score = score
229
245
super ().__init__ ()
230
246
231
- def get_search_fields (self ):
232
- return {self .path }
247
+ def get_search_fields (self , compiler , connection ):
248
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
233
249
234
- def search_operator (self ):
250
+ def search_operator (self , compiler , connection ):
235
251
params = {
236
- "path" : self .path ,
252
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
237
253
}
238
254
if self .score is not None :
239
- params ["score" ] = self .score
255
+ params ["score" ] = self .score . definitions
240
256
if self .lt is not None :
241
257
params ["lt" ] = self .lt
242
258
if self .lte is not None :
@@ -256,16 +272,16 @@ def __init__(self, path, query, allow_analyzed_field=None, score=None):
256
272
self .score = score
257
273
super ().__init__ ()
258
274
259
- def get_search_fields (self ):
260
- return {self .path }
275
+ def get_search_fields (self , compiler , connection ):
276
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
261
277
262
- def search_operator (self ):
278
+ def search_operator (self , compiler , connection ):
263
279
params = {
264
- "path" : self .path ,
265
- "query" : self .query ,
280
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
281
+ "query" : self .query . as_mql ( compiler , connection , as_path = True ) ,
266
282
}
267
283
if self .score :
268
- params ["score" ] = self .score
284
+ params ["score" ] = self .score . definitions
269
285
if self .allow_analyzed_field is not None :
270
286
params ["allowAnalyzedField" ] = self .allow_analyzed_field
271
287
return {"regex" : params }
@@ -281,16 +297,16 @@ def __init__(self, path, query, fuzzy=None, match_criteria=None, synonyms=None,
281
297
self .score = score
282
298
super ().__init__ ()
283
299
284
- def get_search_fields (self ):
285
- return {self .path }
300
+ def get_search_fields (self , compiler , connection ):
301
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
286
302
287
- def search_operator (self ):
303
+ def search_operator (self , compiler , connection ):
288
304
params = {
289
- "path" : self .path ,
290
- "query" : self .query ,
305
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
306
+ "query" : self .query . as_mql ( compiler , connection , as_path = True ) ,
291
307
}
292
308
if self .score :
293
- params ["score" ] = self .score
309
+ params ["score" ] = self .score . definitions
294
310
if self .fuzzy is not None :
295
311
params ["fuzzy" ] = self .fuzzy
296
312
if self .match_criteria is not None :
@@ -308,16 +324,16 @@ def __init__(self, path, query, allow_analyzed_field=None, score=None):
308
324
self .score = score
309
325
super ().__init__ ()
310
326
311
- def get_search_fields (self ):
312
- return {self .path }
327
+ def get_search_fields (self , compiler , connection ):
328
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
313
329
314
- def search_operator (self ):
330
+ def search_operator (self , compiler , connection ):
315
331
params = {
316
- "path" : self .path ,
317
- "query" : self .query ,
332
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
333
+ "query" : self .query . as_mql ( compiler , connection , as_path = True ) ,
318
334
}
319
335
if self .score :
320
- params ["score" ] = self .score
336
+ params ["score" ] = self .score . definitions
321
337
if self .allow_analyzed_field is not None :
322
338
params ["allowAnalyzedField" ] = self .allow_analyzed_field
323
339
return {"wildcard" : params }
@@ -331,17 +347,17 @@ def __init__(self, path, relation, geometry, score=None):
331
347
self .score = score
332
348
super ().__init__ ()
333
349
334
- def get_search_fields (self ):
335
- return {self .path }
350
+ def get_search_fields (self , compiler , connection ):
351
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
336
352
337
- def search_operator (self ):
353
+ def search_operator (self , compiler , connection ):
338
354
params = {
339
- "path" : self .path ,
355
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
340
356
"relation" : self .relation ,
341
357
"geometry" : self .geometry ,
342
358
}
343
359
if self .score :
344
- params ["score" ] = self .score
360
+ params ["score" ] = self .score . definitions
345
361
return {"geoShape" : params }
346
362
347
363
@@ -353,17 +369,17 @@ def __init__(self, path, kind, geo_object, score=None):
353
369
self .score = score
354
370
super ().__init__ ()
355
371
356
- def search_operator (self ):
372
+ def search_operator (self , compiler , connection ):
357
373
params = {
358
- "path" : self .path ,
374
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
359
375
self .kind : self .geo_object ,
360
376
}
361
377
if self .score :
362
- params ["score" ] = self .score
378
+ params ["score" ] = self .score . definitions
363
379
return {"geoWithin" : params }
364
380
365
- def get_search_fields (self ):
366
- return {self .path }
381
+ def get_search_fields (self , compiler , connection ):
382
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
367
383
368
384
369
385
class SearchMoreLikeThis (SearchExpression ):
@@ -372,15 +388,15 @@ def __init__(self, documents, score=None):
372
388
self .score = score
373
389
super ().__init__ ()
374
390
375
- def search_operator (self ):
391
+ def search_operator (self , compiler , connection ):
376
392
params = {
377
393
"like" : self .documents ,
378
394
}
379
395
if self .score :
380
- params ["score" ] = self .score
396
+ params ["score" ] = self .score . definitions
381
397
return {"moreLikeThis" : params }
382
398
383
- def get_search_fields (self ):
399
+ def get_search_fields (self , compiler , connection ):
384
400
needed_fields = set ()
385
401
for doc in self .documents :
386
402
needed_fields .update (set (doc .keys ()))
@@ -404,13 +420,13 @@ def __init__(
404
420
self .score = score
405
421
self .minimum_should_match = minimum_should_match
406
422
407
- def get_search_fields (self ):
423
+ def get_search_fields (self , compiler , connection ):
408
424
fields = set ()
409
425
for clause in self .must + self .should + self .filter + self .must_not :
410
426
fields .update (clause .get_search_fields ())
411
427
return fields
412
428
413
- def search_operator (self ):
429
+ def search_operator (self , compiler , connection ):
414
430
params = {}
415
431
if self .must :
416
432
params ["must" ] = [clause .search_operator () for clause in self .must ]
@@ -491,8 +507,8 @@ def __or__(self, other):
491
507
def __ror__ (self , other ):
492
508
raise NotSupportedError ("SearchVector cannot be combined" )
493
509
494
- def get_search_fields (self ):
495
- return {self .path }
510
+ def get_search_fields (self , compiler , connection ):
511
+ return {self .path . as_mql ( compiler , connection , as_path = True ) }
496
512
497
513
def _get_query_index (self , fields , compiler ):
498
514
for search_indexes in compiler .collection .list_search_indexes ():
@@ -507,7 +523,7 @@ def _get_query_index(self, fields, compiler):
507
523
def as_mql (self , compiler , connection ):
508
524
params = {
509
525
"index" : self ._get_query_index (self .get_search_fields (), compiler ),
510
- "path" : self .path ,
526
+ "path" : self .path . as_mql ( compiler , connection , as_path = True ) ,
511
527
"queryVector" : self .query_vector ,
512
528
"limit" : self .limit ,
513
529
}
0 commit comments