[FIX] models: order by translatable fields with multiple translations
When a model list is ordered on a translatable field, a join on the `ir_translation` is done, so the order is correctly done on the translation value rather than the translation source (in English). This join wasn't entirely correct: If there were multiple translations for a same record field (which should not happen, but which is not forbidden) e.g. Two different translations for the name of a same product, then this record appeared several times in the result of the query, while this is expected to only have each record once (Obviously, when you perform a search on products, you do not expect to have multiple times the same products returned by the search query) This had as side-effect, when searching records with a limit of 80 items, to return actually less than 80 items (e.g. 72 products, with multiple translations), and, as the limit of the search was not reached (e.g. 72 instead of 80), the web client did not perform the `search_count`, as it does not do it when the limit of the search is not reached (meaning there is no more items than what has been returned by the search query), and therefore the web client did not consider there was actually more results, more pages of results. In summary, the count of records of a model could be different when performing the order on a translatable field from one language to another. (e.g. A database could return 150 products in English, but only 72 in Spanish). To solve this issue, we have required the use of the operator `DISTINCT ON`, which provides the possibility to keep only the first translation for each record according to a given order (in this case, we keep the translation that has been added the most recently) From the PostgreSQL documentation: DISTINCT ON ( expression [, ...] ) keeps only the first row of each set of rows where the given expressions evaluate to equal. The DISTINCT ON expressions are interpreted using the same rules as for ORDER BY (see above). Note that the "first row" of each set is unpredictable unless ORDER BY is used to ensure that the desired row appears first The performance of this new query is similar to the performance of the former query. opw-666071
This commit is contained in:
+15
-2
@@ -4595,11 +4595,24 @@ class BaseModel(object):
|
||||
"""
|
||||
lang = self._context.get('lang')
|
||||
if lang and lang != 'en_US':
|
||||
# Sub-select to return at most one translation per record.
|
||||
# Even if it shoud probably not be the case,
|
||||
# this is possible to have multiple translations for a same record in the same language.
|
||||
# The parenthesis surrounding the select are important, as this is a sub-select.
|
||||
# The quotes surrounding `ir_translation` are important as well.
|
||||
unique_translation_subselect = """
|
||||
(SELECT DISTINCT ON (res_id) res_id, value
|
||||
FROM "ir_translation"
|
||||
WHERE
|
||||
name = %s AND
|
||||
lang = %s AND
|
||||
value != %s
|
||||
ORDER BY res_id, id DESC)
|
||||
"""
|
||||
alias, alias_statement = query.add_join(
|
||||
(table_alias, 'ir_translation', 'id', 'res_id', field),
|
||||
(table_alias, unique_translation_subselect, 'id', 'res_id', field),
|
||||
implicit=False,
|
||||
outer=True,
|
||||
extra='"{rhs}"."name" = %s AND "{rhs}"."lang" = %s AND "{rhs}"."value" != %s',
|
||||
extra_params=["%s,%s" % (self._name, field), lang, ""],
|
||||
)
|
||||
return 'COALESCE("%s"."%s", "%s"."%s")' % (alias, 'value', table_alias, field)
|
||||
|
||||
@@ -132,8 +132,8 @@ class Query(object):
|
||||
self.tables.append(alias_statement)
|
||||
join_tuple = (alias, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN')
|
||||
self.joins.setdefault(lhs, []).append(join_tuple)
|
||||
if extra:
|
||||
extra = extra.format(lhs=lhs, rhs=alias)
|
||||
if extra or extra_params:
|
||||
extra = (extra or '').format(lhs=lhs, rhs=alias)
|
||||
self.extras[(lhs, join_tuple)] = (extra, extra_params)
|
||||
return alias, alias_statement
|
||||
|
||||
@@ -152,9 +152,11 @@ class Query(object):
|
||||
(join, alias_mapping[rhs], lhs, lhs_col, rhs, rhs_col))
|
||||
extra = self.extras.get((lhs, (rhs, lhs_col, rhs_col, join)))
|
||||
if extra:
|
||||
from_clause.append(' AND ')
|
||||
from_clause.append(extra[0])
|
||||
from_params.extend(extra[1])
|
||||
if extra[0]:
|
||||
from_clause.append(' AND ')
|
||||
from_clause.append(extra[0])
|
||||
if extra[1]:
|
||||
from_params.extend(extra[1])
|
||||
from_clause.append(')')
|
||||
add_joins_for_table(rhs)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user