Skip to content

Commit f31ee2d

Browse files
committed
Add search index test
1 parent ca8a7cf commit f31ee2d

File tree

4 files changed

+334
-71
lines changed

4 files changed

+334
-71
lines changed

django_mongodb_backend/compiler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from django.utils.functional import cached_property
1818
from pymongo import ASCENDING, DESCENDING
1919

20-
from .functions import SearchExpression
20+
from .expressions.builtins import SearchExpression
2121
from .query import MongoQuery, wrap_database_errors
2222

2323

django_mongodb_backend/expressions/builtins.py

Lines changed: 83 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,7 @@ def value(self, compiler, connection): # noqa: ARG001
214214

215215

216216
class SearchExpression(Expression):
217-
def __init__(self):
218-
super().__init__(output_field=FloatField())
217+
output_field = FloatField()
219218

220219
def get_source_expressions(self):
221220
return []
@@ -240,34 +239,37 @@ def _get_query_index(self, fields, compiler):
240239

241240

242241
class SearchAutocomplete(SearchExpression):
243-
def __init__(self, path, query, score=None):
244-
self.path = F(path)
245-
self.query = Value(query)
242+
def __init__(self, path, query, fuzzy=None, score=None):
243+
self.path = path
244+
self.query = query
245+
self.fuzzy = fuzzy
246246
self.score = score
247247
super().__init__()
248248

249249
def as_mql(self, compiler, connection):
250250
params = {
251-
"path": self.path.as_mql(compiler, connection)[1:],
252-
"query": self.query.as_mql(compiler, connection),
251+
"path": self.path,
252+
"query": self.query,
253253
}
254254
if self.score is not None:
255255
params["score"] = self.score
256+
if self.fuzzy is not None:
257+
params["fuzzy"] = self.fuzzy
256258
index = self._get_query_index([self.path], compiler)
257259
return {"$search": {"autocomplete": params, "index": index}}
258260

259261

260262
class SearchEquals(SearchExpression):
261263
def __init__(self, path, value, score=None):
262-
self.path = F(path)
263-
self.value = Value(query)
264+
self.path = path
265+
self.value = value
264266
self.score = score
265267
super().__init__()
266268

267269
def as_mql(self, compiler, connection):
268270
params = {
269-
"path": self.path.as_mql(compiler, connection)[1:],
270-
"value": self.value.as_mql(compiler, connection),
271+
"path": self.path,
272+
"value": self.value,
271273
}
272274
if self.score is not None:
273275
params["score"] = self.score
@@ -277,13 +279,13 @@ def as_mql(self, compiler, connection):
277279

278280
class SearchExists(SearchExpression):
279281
def __init__(self, path, score=None):
280-
self.path = F(path)
282+
self.path = path
281283
self.score = score
282284
super().__init__()
283285

284286
def as_mql(self, compiler, connection):
285287
params = {
286-
"path": self.path.as_mql(compiler, connection)[1:],
288+
"path": self.path,
287289
}
288290
if self.score is not None:
289291
params["score"] = self.score
@@ -293,15 +295,15 @@ def as_mql(self, compiler, connection):
293295

294296
class SearchIn(SearchExpression):
295297
def __init__(self, path, value, score=None):
296-
self.path = F(path)
297-
self.value = Value(value)
298+
self.path = path
299+
self.value = value
298300
self.score = score
299301
super().__init__()
300302

301303
def as_mql(self, compiler, connection):
302304
params = {
303-
"path": self.path.as_mql(compiler, connection)[1:],
304-
"value": self.value.as_mql(compiler, connection),
305+
"path": self.path,
306+
"value": self.value,
305307
}
306308
if self.score is not None:
307309
params["score"] = self.score
@@ -310,18 +312,18 @@ def as_mql(self, compiler, connection):
310312

311313

312314
class SearchPhrase(SearchExpression):
313-
def __init__(self, path, value, slop=None, synonyms=None, score=None):
314-
self.path = F(path)
315-
self.value = Value(value)
315+
def __init__(self, path, query, slop=None, synonyms=None, score=None):
316+
self.path = path
317+
self.query = query
316318
self.score = score
317319
self.slop = slop
318320
self.synonyms = synonyms
319321
super().__init__()
320322

321323
def as_mql(self, compiler, connection):
322324
params = {
323-
"path": self.path.as_mql(compiler, connection)[1:],
324-
"value": self.value.as_mql(compiler, connection),
325+
"path": self.path,
326+
"query": self.query,
325327
}
326328
if self.score is not None:
327329
params["score"] = self.score
@@ -335,15 +337,15 @@ def as_mql(self, compiler, connection):
335337

336338
class SearchQueryString(SearchExpression):
337339
def __init__(self, path, query, score=None):
338-
self.path = F(path)
339-
self.query = Value(query)
340+
self.path = path
341+
self.query = query
340342
self.score = score
341343
super().__init__()
342344

343345
def as_mql(self, compiler, connection):
344346
params = {
345-
"defaultPath": self.path.as_mql(compiler, connection)[1:],
346-
"query": self.query.as_mql(compiler, connection),
347+
"defaultPath": self.path,
348+
"query": self.query,
347349
}
348350
if self.score is not None:
349351
params["score"] = self.score
@@ -353,132 +355,138 @@ def as_mql(self, compiler, connection):
353355

354356
class SearchRange(SearchExpression):
355357
def __init__(self, path, lt=None, lte=None, gt=None, gte=None, score=None):
356-
self.path = F(path)
357-
self.lt = Value(lt)
358-
self.lte = Value(lte)
359-
self.gt = Value(gt)
360-
self.gte = Value(gte)
358+
self.path = path
359+
self.lt = lt
360+
self.lte = lte
361+
self.gt = gt
362+
self.gte = gte
361363
self.score = score
362364
super().__init__()
363365

364366
def as_mql(self, compiler, connection):
365367
params = {
366-
"path": self.path.as_mql(compiler, connection)[1:],
368+
"path": self.path,
367369
}
368370
if self.score is not None:
369371
params["score"] = self.score
370372
if self.lt is not None:
371-
params["lt"] = self.lt.as_mql(compiler, connection)
373+
params["lt"] = self.lt
372374
if self.lte is not None:
373-
params["lte"] = self.lte.as_mql(compiler, connection)
375+
params["lte"] = self.lte
374376
if self.gt is not None:
375-
params["gt"] = self.gt.as_mql(compiler, connection)
377+
params["gt"] = self.gt
376378
if self.gte is not None:
377-
params["gte"] = self.gte.as_mql(compiler, connection)
379+
params["gte"] = self.gte
378380
index = self._get_query_index([self.path], compiler)
379381
return {"$search": {"range": params, "index": index}}
380382

381383

382384
class SearchRegex(SearchExpression):
383385
def __init__(self, path, query, allow_analyzed_field=None, score=None):
384-
self.path = F(path)
385-
self.allow_analyzed_field = Value(allow_analyzed_field)
386+
self.path = path
387+
self.query = query
388+
self.allow_analyzed_field = allow_analyzed_field
386389
self.score = score
387390
super().__init__()
388391

389392
def as_mql(self, compiler, connection):
390393
params = {
391-
"path": self.path.as_mql(compiler, connection)[1:],
394+
"path": self.path,
395+
"query": self.query,
392396
}
393397
if self.score:
394398
params["score"] = self.score
395399
if self.allow_analyzed_field is not None:
396-
params["allowAnalyzedField"] = self.allow_analyzed_field.as_mql(compiler, connection)
400+
params["allowAnalyzedField"] = self.allow_analyzed_field
397401
index = self._get_query_index([self.path], compiler)
398402
return {"$search": {"regex": params, "index": index}}
399403

400404

401405
class SearchText(SearchExpression):
402406
def __init__(self, path, query, fuzzy=None, match_criteria=None, synonyms=None, score=None):
403-
self.path = F(path)
404-
self.fuzzy = Value(fuzzy)
405-
self.match_criteria = Value(match_criteria)
406-
self.synonyms = Value(synonyms)
407+
self.path = path
408+
self.query = query
409+
self.fuzzy = fuzzy
410+
self.match_criteria = match_criteria
411+
self.synonyms = synonyms
407412
self.score = score
408413
super().__init__()
409414

410415
def as_mql(self, compiler, connection):
411416
params = {
412-
"path": self.path.as_mql(compiler, connection)[1:],
417+
"path": self.path,
418+
"query": self.query,
413419
}
414420
if self.score:
415421
params["score"] = self.score
416422
if self.fuzzy is not None:
417-
params["fuzzy"] = self.fuzzy.as_mql(compiler, connection)
423+
params["fuzzy"] = self.fuzzy
418424
if self.match_criteria is not None:
419-
params["matchCriteria"] = self.match_criteria.as_mql(compiler, connection)
425+
params["matchCriteria"] = self.match_criteria
420426
if self.synonyms is not None:
421-
params["synonyms"] = self.synonyms.as_mql(compiler, connection)
427+
params["synonyms"] = self.synonyms
422428
index = self._get_query_index([self.path], compiler)
423429
return {"$search": {"text": params, "index": index}}
424430

425431

426432
class SearchWildcard(SearchExpression):
427433
def __init__(self, path, query, allow_analyzed_field=None, score=None):
428-
self.path = F(path)
429-
self.allow_analyzed_field = Value(allow_analyzed_field)
434+
self.path = path
435+
self.query = query
436+
self.allow_analyzed_field = allow_analyzed_field
430437
self.score = score
431438
super().__init__()
432439

433440
def as_mql(self, compiler, connection):
434441
params = {
435-
"path": self.path.as_mql(compiler, connection)[1:],
442+
"path": self.path,
443+
"query": self.query,
436444
}
437445
if self.score:
438446
params["score"] = self.score
439447
if self.allow_analyzed_field is not None:
440-
params["allowAnalyzedField"] = self.allow_analyzed_field.as_mql(compiler, connection)
448+
params["allowAnalyzedField"] = self.allow_analyzed_field
441449
index = self._get_query_index([self.path], compiler)
442450
return {"$search": {"wildcard": params, "index": index}}
443451

444452

445453
class SearchGeoShape(SearchExpression):
446454
def __init__(self, path, relation, geometry, score=None):
447-
self.path = F(path)
455+
self.path = path
448456
self.relation = relation
449457
self.geometry = geometry
450458
self.score = score
451459
super().__init__()
452460

453461
def as_mql(self, compiler, connection):
454462
params = {
455-
"path": self.path.as_mql(compiler, connection)[1:],
463+
"path": self.path,
456464
"relation": self.relation,
457465
"geometry": self.geometry,
458466
}
459467
if self.score:
460468
params["score"] = self.score
461469
index = self._get_query_index([self.path], compiler)
462-
return {"$search": {"wildcard": params, "index": index}}
470+
return {"$search": {"geoShape": params, "index": index}}
463471

464472

465473
class SearchGeoWithin(SearchExpression):
466-
def __init__(self, path, kind, geo_object, geometry, score=None):
467-
self.path = F(path)
474+
def __init__(self, path, kind, geo_object, score=None):
475+
self.path = path
468476
self.kind = kind
469477
self.geo_object = geo_object
470478
self.score = score
471479
super().__init__()
472480

473481
def as_mql(self, compiler, connection):
474482
params = {
475-
"path": self.path.as_mql(compiler, connection)[1:],
483+
"path": self.path,
476484
self.kind: self.geo_object,
477485
}
478486
if self.score:
479487
params["score"] = self.score
480488
index = self._get_query_index([self.path], compiler)
481-
return {"$search": {"wildcard": params, "index": index}}
489+
return {"$search": {"geoWithin": params, "index": index}}
482490

483491

484492
class SearchMoreLikeThis(SearchExpression):
@@ -497,7 +505,22 @@ def as_mql(self, compiler, connection):
497505
for doc in self.documents:
498506
needed_fields += list(doc.keys())
499507
index = self._get_query_index(needed_fields, compiler)
500-
return {"$search": {"wildcard": params, "index": index}}
508+
return {"$search": {"moreLikeThis": params, "index": index}}
509+
510+
511+
class SearchScoreOption:
512+
"""Class to mutate scoring on a search operation"""
513+
514+
def __init__(self, definitions=None):
515+
self.definitions = definitions
516+
517+
518+
class CombinedSearchExpression(SearchExpression):
519+
def __init__(self, lhs, connector, rhs, output_field=None):
520+
super().__init__(output_field=output_field)
521+
self.connector = connector
522+
self.lhs = lhs
523+
self.rhs = rhs
501524

502525

503526
def register_expressions():

tests/queries_/models.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from django.db import models
22

33
from django_mongodb_backend.fields import ObjectIdAutoField, ObjectIdField
4-
from django_mongodb_backend.indexes import SearchIndex
54

65

76
class Author(models.Model):
@@ -60,6 +59,4 @@ class Article(models.Model):
6059
headline = models.CharField(max_length=100)
6160
number = models.IntegerField()
6261
body = models.TextField()
63-
64-
class Meta:
65-
indexes = [SearchIndex(fields=["headline"])]
62+
location = models.JSONField(null=True)

0 commit comments

Comments
 (0)