[IMP] core: improve search count with a limit

The new feature (search count with limit) introduced by https://github.com/odoo/odoo/pull/95589 lacks of test and code readability:
- Add tests to check the result and query generate
- Increase the readability of SQL/python code.
- Change a little bit the SQL request generate: in the subquery, change `SELECT 1 FROM...` into `SELECT  FROM` which avoid extra work in the postgreSQL side.

task-2761165

closes odoo/odoo#95641

Signed-off-by: Raphael Collet <rco@odoo.com>
This commit is contained in:
Rémy Voet (ryv)
2022-07-08 17:10:51 +02:00
parent c471f96c93
commit 193082f6ac
3 changed files with 42 additions and 6 deletions
+14 -1
View File
@@ -1144,12 +1144,25 @@ class TestQueries(TransactionCase):
Model.search([('name', 'like', 'foo')])
with self.assertQueries(['''
SELECT count(*)
SELECT COUNT(*)
FROM "res_partner"
WHERE (("res_partner"."active" = %s) AND ("res_partner"."name"::text LIKE %s))
''']):
Model.search_count([('name', 'like', 'foo')])
def test_count_limit(self):
Model = self.env['res.partner']
Model.search([('name', 'like', 'foo')])
with self.assertQueries(['''
SELECT COUNT(*) FROM (
SELECT FROM "res_partner"
WHERE (("res_partner"."active" = %s) AND ("res_partner"."name"::text LIKE %s))
LIMIT 1
) t
''']):
Model.search_count([('name', 'like', 'foo')], limit=1)
def test_translated_field(self):
self.env['res.lang']._activate_lang('fr_FR')
Model = self.env['res.partner.title'].with_context(lang='fr_FR')
+15
View File
@@ -192,3 +192,18 @@ class test_search(TransactionCase):
})
cc_search = model_bank.search([('name', '=', 'Crédit Communal')])
self.assertNotIn(bank_credit_communal, cc_search, "Search for inactive record with x_active set to True has failed")
def test_21_search_count(self):
Partner = self.env['res.partner']
count_partner_before = Partner.search_count([])
partners = Partner.create([
{'name': 'abc'},
{'name': 'zer'},
{'name': 'christope'},
{'name': 'runbot'},
])
self.assertEqual(len(partners) + count_partner_before, Partner.search_count([]))
self.assertEqual(len(partners) + count_partner_before, Partner.search([], count=True))
self.assertEqual(3, Partner.search_count([], limit=3))
self.assertEqual(3, Partner.search([], count=True, limit=3))
+13 -5
View File
@@ -1872,6 +1872,10 @@ class BaseModel(metaclass=MetaModel):
Returns the number of records in the current model matching :ref:`the
provided domain <reference/orm/domains>`.
:param domain: :ref:`A search domain <reference/orm/domains>`. Use an empty
list to match all records.
:param limit: maximum number of record to count (upperbound) (default: all)
"""
res = self.search(domain, limit=limit, count=True)
return res if isinstance(res, int) else len(res)
@@ -4888,14 +4892,18 @@ class BaseModel(metaclass=MetaModel):
query.limit = limit
if count:
# Ignore order, limit and offset when just counting, they don't make sense and could
# Ignore order and offset when just counting, they don't make sense and could
# hurt performance
query_str, params = query.select(limit and "1" or "count(*)")
if limit:
query_str = "select count(*) from (" + query_str + ") t"
# Special case to avoid counting every record in DB (which can be really slow).
# The result will be between 0 and limit.
query_str, params = query.select("") # generates a `SELECT FROM` (faster)
query_str = f"SELECT COUNT(*) FROM ({query_str}) t"
else:
query_str, params = query.select("COUNT(*)")
self._cr.execute(query_str, params)
res = self._cr.fetchone()
return res[0]
return self._cr.fetchone()[0]
query.order = self._generate_order_by(order, query).replace('ORDER BY ', '')
query.offset = offset