[FIX] core: fix search for translated field

make searching translated field language dependent
add an extra filter using trigram index to speed up '=' and 'like' search

X-original-commit: 5b6f0a9e5f9d60735e306709a3e7d7ac46a586be
Part-of: odoo/odoo#103031
This commit is contained in:
Chong Wang (cwg)
2022-10-11 14:10:15 +02:00
parent bde30899a4
commit be1eed6cd7
3 changed files with 32 additions and 14 deletions
+2 -1
View File
@@ -235,11 +235,12 @@ class Page(models.Model):
FROM {table}
LEFT JOIN ir_ui_view v ON {table}.view_id = v.id
WHERE v.name ILIKE {search}
OR jsonb_path_query_array(v.arch_db,'$.*')::text ILIKE {search}
OR COALESCE(v.arch_db->>{lang}, v.arch_db->>'en_US') ILIKE {search}
LIMIT {limit}
""").format(
table=sql.Identifier(self._table),
search=sql.Placeholder('search'),
lang=sql.Literal(self.env.lang or 'en_US'),
limit=sql.Placeholder('limit'),
)
self.env.cr.execute(query, {
+6 -3
View File
@@ -1171,7 +1171,7 @@ class TestQueries(TransactionCase):
with self.assertQueries(['''
SELECT "res_partner_title".id
FROM "res_partner_title"
WHERE jsonb_path_query_array("res_partner_title"."name", '$.*')::text like %s
WHERE COALESCE("res_partner_title"."name"->>'fr_FR', "res_partner_title"."name"->>'en_US') like %s
ORDER BY COALESCE("res_partner_title"."name"->>'fr_FR', "res_partner_title"."name"->>'en_US')
''']):
Model.search([('name', 'like', 'foo')])
@@ -1220,7 +1220,10 @@ class TestQueries(TransactionCase):
with self.assertQueries(['''
SELECT "ir_model".id
FROM "ir_model"
WHERE (jsonb_path_query_array("ir_model"."name",'$.*')::text ILIKE %s OR ("ir_model"."model"::text ILIKE %s))
WHERE (
"ir_model"."name"->>'en_US' ILIKE %s
OR ("ir_model"."model"::text ILIKE %s)
)
ORDER BY "ir_model"."model"
LIMIT 100
''']):
@@ -1231,7 +1234,7 @@ class TestQueries(TransactionCase):
SELECT "ir_model".id
FROM "ir_model"
WHERE (
jsonb_path_query_array("ir_model"."name",'$.*')::text NOT ILIKE %s
"ir_model"."name"->>'en_US' NOT ILIKE %s
AND (("ir_model"."model"::text NOT ILIKE %s) OR "ir_model"."model" IS NULL)
)
ORDER BY "ir_model"."model"
+24 -10
View File
@@ -433,6 +433,7 @@ class expression(object):
:attr query: Query object holding the final result
"""
self._unaccent_wrapper = get_unaccent_wrapper(model._cr)
self._has_trigram = model.pool.has_trigram
self.root_model = model
self.root_alias = alias or model._table
@@ -960,22 +961,35 @@ class expression(object):
push_result(expr, params)
elif field.translate and right:
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator)
expr = ''
params = []
if self._has_trigram and field.index == 'trigram' and sql_operator in ('=', 'like', 'ilike'):
# a prefilter using trigram index to speed up '=', 'like', 'ilike'
# '!=', '<=', '<', '>', '>=', 'in', 'not in', 'not like', 'not ilike' cannot use this trick
_unaccent = self._unaccent(field)
_left = _unaccent(f'''jsonb_path_query_array("{alias}"."{left}", '$.*')::text''')
_sql_operator = 'like' if sql_operator == '=' else sql_operator
expr = f"{_left} {_sql_operator} {_unaccent('%s')} AND "
params.append(f'%{right}%')
unaccent = self._unaccent(field) if sql_operator.endswith('like') else lambda x: x
lang = model.env.lang or 'en_US'
if lang == 'en_US':
left = unaccent(f""""{alias}"."{left}"->>'en_US'""")
else:
left = unaccent(f'''COALESCE("{alias}"."{left}"->>'{lang}', "{alias}"."{left}"->>'en_US')''')
need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike')
if need_wildcard:
right = '%%%s%%' % right
if sql_operator in ('in', 'not in'):
right = tuple(right)
unaccent = self._unaccent(field) if sql_operator.endswith('like') else lambda x: x
if need_wildcard:
# Inactive languages are not removed in DB and can be searched by using this function
left = unaccent(f'jsonb_path_query_array("{alias}"."{left}", \'$.*\')::text')
else:
lang = model.env.lang or 'en_US'
left = unaccent(f'COALESCE("{alias}"."{left}"->>\'{lang}\', "{alias}"."{left}"->>\'en_US\')')
instr = unaccent('%s')
push_result(f"{left} {sql_operator} {instr}", [right])
expr += f"{left} {sql_operator} {unaccent('%s')}"
params.append(right)
push_result(expr, params)
else:
expr, params = self.__leaf_to_sql(leaf, model, alias)