Fixed #29582 -- Fixed a crash when using SearchVector with non text-fields.

The PostgreSQL concat() function handles nulls and non-text values better than
the || operator.
This commit is contained in:
Simon Charette 2018-07-20 21:23:05 -04:00
parent 71a739f3d7
commit 1a28dc3887
2 changed files with 9 additions and 6 deletions

View File

@ -1,6 +1,5 @@
from django.db.models import Field, FloatField from django.db.models import Field, FloatField
from django.db.models.expressions import CombinedExpression, Func, Value from django.db.models.expressions import CombinedExpression, Func, Value
from django.db.models.functions import Coalesce
from django.db.models.lookups import Lookup from django.db.models.lookups import Lookup
@ -46,15 +45,13 @@ class SearchVectorCombinable:
class SearchVector(SearchVectorCombinable, Func): class SearchVector(SearchVectorCombinable, Func):
function = 'to_tsvector' function = 'to_tsvector'
arg_joiner = " || ' ' || " arg_joiner = ", ' ',"
template = '%(function)s(concat(%(expressions)s))'
output_field = SearchVectorField() output_field = SearchVectorField()
config = None config = None
def __init__(self, *expressions, **extra): def __init__(self, *expressions, **extra):
super().__init__(*expressions, **extra) super().__init__(*expressions, **extra)
self.source_expressions = [
Coalesce(expression, Value('')) for expression in self.source_expressions
]
self.config = self.extra.get('config', self.config) self.config = self.extra.get('config', self.config)
weight = self.extra.get('weight') weight = self.extra.get('weight')
if weight is not None and not hasattr(weight, 'resolve_expression'): if weight is not None and not hasattr(weight, 'resolve_expression'):
@ -75,7 +72,7 @@ class SearchVector(SearchVectorCombinable, Func):
if template is None: if template is None:
if self.config: if self.config:
config_sql, config_params = compiler.compile(self.config) config_sql, config_params = compiler.compile(self.config)
template = "%(function)s({}::regconfig, %(expressions)s)".format(config_sql.replace('%', '%%')) template = "%(function)s({}::regconfig, concat(%(expressions)s))".format(config_sql.replace('%', '%%'))
else: else:
template = self.template template = self.template
sql, params = super().as_sql(compiler, connection, function=function, template=template) sql, params = super().as_sql(compiler, connection, function=function, template=template)

View File

@ -155,6 +155,12 @@ class MultipleFieldsTest(GrailTestData, PostgreSQLTestCase):
).filter(search='bedemir') ).filter(search='bedemir')
self.assertEqual(set(searched), {self.bedemir0, self.bedemir1, self.crowd, self.witch, self.duck}) self.assertEqual(set(searched), {self.bedemir0, self.bedemir1, self.crowd, self.witch, self.duck})
def test_search_with_non_text(self):
searched = Line.objects.annotate(
search=SearchVector('id'),
).filter(search=str(self.crowd.id))
self.assertSequenceEqual(searched, [self.crowd])
def test_config_query_explicit(self): def test_config_query_explicit(self):
searched = Line.objects.annotate( searched = Line.objects.annotate(
search=SearchVector('scene__setting', 'dialogue', config='french'), search=SearchVector('scene__setting', 'dialogue', config='french'),