[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:
@@ -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, {
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user