diff --git a/addons/website/models/website_page.py b/addons/website/models/website_page.py index 5e65ea34821..52bc3f65c4a 100644 --- a/addons/website/models/website_page.py +++ b/addons/website/models/website_page.py @@ -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, { diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py index f0e64eb6cc8..7703d70bbc0 100644 --- a/odoo/addons/base/tests/test_expression.py +++ b/odoo/addons/base/tests/test_expression.py @@ -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" diff --git a/odoo/osv/expression.py b/odoo/osv/expression.py index 59aa704d66b..01a04f64d09 100644 --- a/odoo/osv/expression.py +++ b/odoo/osv/expression.py @@ -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)