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.expressions import CombinedExpression, Func, Value
from django.db.models.functions import Coalesce
from django.db.models.lookups import Lookup
@ -46,15 +45,13 @@ class SearchVectorCombinable:
class SearchVector(SearchVectorCombinable, Func):
function = 'to_tsvector'
arg_joiner = " || ' ' || "
arg_joiner = ", ' ',"
template = '%(function)s(concat(%(expressions)s))'
output_field = SearchVectorField()
config = None
def __init__(self, *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)
weight = self.extra.get('weight')
if weight is not None and not hasattr(weight, 'resolve_expression'):
@ -75,7 +72,7 @@ class SearchVector(SearchVectorCombinable, Func):
if template is None:
if 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:
template = self.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')
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):
searched = Line.objects.annotate(
search=SearchVector('scene__setting', 'dialogue', config='french'),