[IMP] website: filter out unpublished records for non internal users

Issue:
Search on website has a big performance issue because the database has
too many product_template.

Analyze :
The website search mechanism has hardcoded sql query. And the generic
query don't have any filtering. Which lead to make a expensive search
(similarity) on the whole product_template even if the products are
not published and then displayed to the user.

Fix :
Filter the is_published field in the generic website method when the
model has a 'is_published' field and the request is not done by a user
(thus for customers or portal users).

Note:
The internal user is still able to search on unpublished product.Thus
have no performance improvement.

Benchmark:
| SQL Query | # Input data | Before PR | After PR |
|:---------:|:------------:|:---------:|:--------:|
|General best_similarity |700000 products (161 published) | 12.65 s | 0.12 s |
|FROM ir_translation|2,796,000 ir_translation | 6.338 s (586k hit) | 0.091 s (185 hit) |

Related task:
task-3473786

Related ticket:
opw-3418457

closes odoo/odoo#136080

X-original-commit: fcb968836a2d0e49516fe3ecb23dde61f4597749
Signed-off-by: Romain Derie (rde) <rde@odoo.com>
This commit is contained in:
Benjamin Hanquin (beha)
2023-09-20 17:18:10 +00:00
parent 342c88e5ef
commit e5a6a4f48a
+14
View File
@@ -1769,6 +1769,18 @@ class Website(models.Model):
similarities=sql.SQL(', ').join(similarities)
)
where_clause = sql.SQL("")
# Filter unpublished records for portal and public user for
# performance.
# TODO: Same for `active` field?
filter_is_published = (
'is_published' in model._fields
and model._fields['is_published'].base_field.model_name == model_name
and not self.env.user.has_group('base.group_user')
)
if filter_is_published:
where_clause = sql.SQL("WHERE is_published")
from_clause = sql.SQL("FROM {table}").format(table=sql.Identifier(model._table))
# Specific handling for fields being actually part of another model
# through the `inherits` mechanism.
@@ -1787,12 +1799,14 @@ class Website(models.Model):
query = sql.SQL("""
SELECT {table}.id, {best_similarity} AS _best_similarity
{from_clause}
{where_clause}
ORDER BY _best_similarity desc
LIMIT 1000
""").format(
table=sql.Identifier(model._table),
best_similarity=best_similarity,
from_clause=from_clause,
where_clause=where_clause,
)
self.env.cr.execute(query, {'search': search})
ids = {row[0] for row in self.env.cr.fetchall() if row[1] and row[1] >= similarity_threshold}