feat: support countig till a limit
In InnoDB counting is essentially O(n) operation, it can be pretty fast on indexes but when filters don't use any index it usually means doing a full table scan. Adding a limit will stop the scan as soon as that many records are matched.
This commit is contained in:
parent
ae649aadf0
commit
a49fafbf8e
4 changed files with 35 additions and 7 deletions
|
|
@ -52,14 +52,24 @@ def get_count() -> int:
|
|||
|
||||
if is_virtual_doctype(args.doctype):
|
||||
controller = get_controller(args.doctype)
|
||||
data = frappe.call(controller.get_count, args=args, **args)
|
||||
count = frappe.call(controller.get_count, args=args, **args)
|
||||
else:
|
||||
args.distinct = sbool(args.distinct)
|
||||
distinct = "distinct " if args.distinct else ""
|
||||
args.fields = [f"count({distinct}`tab{args.doctype}`.name) as total_count"]
|
||||
data = execute(**args)[0].get("total_count")
|
||||
args.limit = cint(args.limit)
|
||||
if args.limit:
|
||||
# Only "count until this limit"
|
||||
args.fields = ["*"]
|
||||
partial_query = execute(**args, run=0)
|
||||
count = frappe.db.sql(
|
||||
f"""with records as ( {partial_query} )
|
||||
select count(*) from records""",
|
||||
)[0][0]
|
||||
else:
|
||||
args.fields = [f"count({distinct}`tab{args.doctype}`.name) as total_count"]
|
||||
count = execute(**args)[0].get("total_count")
|
||||
|
||||
return data
|
||||
return count
|
||||
|
||||
|
||||
def execute(doctype, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ frappe.db = {
|
|||
},
|
||||
count: function (doctype, args = {}) {
|
||||
let filters = args.filters || {};
|
||||
let limit = args.limit;
|
||||
|
||||
// has a filter with childtable?
|
||||
const distinct =
|
||||
|
|
@ -111,6 +112,7 @@ frappe.db = {
|
|||
filters,
|
||||
fields,
|
||||
distinct,
|
||||
limit,
|
||||
});
|
||||
},
|
||||
get_link_options(doctype, txt = "", filters = {}) {
|
||||
|
|
|
|||
|
|
@ -946,6 +946,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
return frappe.db
|
||||
.count(this.doctype, {
|
||||
filters: this.get_filters_for_args(),
|
||||
limit: 1001,
|
||||
})
|
||||
.then((total_count) => {
|
||||
this.total_count = total_count || current_count;
|
||||
|
|
|
|||
|
|
@ -1187,7 +1187,7 @@ class TestReportView(FrappeTestCase):
|
|||
"distinct": "false",
|
||||
}
|
||||
)
|
||||
list_filter_response = execute_cmd("frappe.desk.reportview.get_count")
|
||||
count = execute_cmd("frappe.desk.reportview.get_count")
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
|
|
@ -1196,8 +1196,8 @@ class TestReportView(FrappeTestCase):
|
|||
}
|
||||
)
|
||||
dict_filter_response = execute_cmd("frappe.desk.reportview.get_count")
|
||||
self.assertIsInstance(list_filter_response, int)
|
||||
self.assertEqual(list_filter_response, dict_filter_response)
|
||||
self.assertIsInstance(count, int)
|
||||
self.assertEqual(count, dict_filter_response)
|
||||
|
||||
# test with child table filter
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
|
|
@ -1218,6 +1218,21 @@ class TestReportView(FrappeTestCase):
|
|||
)[0][0]
|
||||
self.assertEqual(child_filter_response, current_value)
|
||||
|
||||
# test with limit
|
||||
limit = 2
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"filters": [["DocType", "is_virtual", "=", 1]],
|
||||
"fields": [],
|
||||
"distinct": "false",
|
||||
"limit": limit,
|
||||
}
|
||||
)
|
||||
count = execute_cmd("frappe.desk.reportview.get_count")
|
||||
self.assertIsInstance(count, int)
|
||||
self.assertLessEqual(count, limit)
|
||||
|
||||
def test_reportview_get(self):
|
||||
user = frappe.get_doc("User", "test@example.com")
|
||||
add_child_table_to_blog_post()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue