Merge pull request #25348 from ankush/estimate_count
perf: cap max count on list views
This commit is contained in:
commit
ee03bf7518
7 changed files with 83 additions and 28 deletions
|
|
@ -37,6 +37,6 @@ context("List Paging", () => {
|
|||
cy.get(".list-paging-area .list-count").should("contain.text", "500 of");
|
||||
cy.get(".list-paging-area .btn-more").click();
|
||||
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "1000 of");
|
||||
cy.get(".list-paging-area .list-count").should("contain.text", "1,000 of");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from frappe.model.base_document import get_controller
|
|||
from frappe.model.db_query import DatabaseQuery
|
||||
from frappe.model.utils import is_virtual_doctype
|
||||
from frappe.utils import add_user_info, cint, format_duration
|
||||
from frappe.utils.data import sbool
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
@ -51,13 +52,22 @@ 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:
|
||||
distinct = "distinct " if args.distinct == "true" else ""
|
||||
args.fields = [f"count({distinct}`tab{args.doctype}`.name) as total_count"]
|
||||
data = execute(**args)[0].get("total_count")
|
||||
args.distinct = sbool(args.distinct)
|
||||
distinct = "distinct " if args.distinct else ""
|
||||
args.limit = cint(args.limit)
|
||||
fieldname = f"{distinct}`tab{args.doctype}`.name"
|
||||
|
||||
return data
|
||||
if args.limit:
|
||||
args.fields = [fieldname]
|
||||
partial_query = execute(**args, run=0)
|
||||
count = frappe.db.sql(f"""select count(*) from ( {partial_query} ) p""")[0][0]
|
||||
else:
|
||||
args.fields = [f"count({fieldname}) as total_count"]
|
||||
count = execute(**args)[0].get("total_count")
|
||||
|
||||
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 = {}) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
this.process_document_refreshes.bind(this),
|
||||
2000
|
||||
);
|
||||
this.count_upper_bound = 1001;
|
||||
}
|
||||
|
||||
has_permissions() {
|
||||
|
|
@ -500,9 +501,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
freeze() {
|
||||
if (this.list_view_settings && !this.list_view_settings.disable_count) {
|
||||
this.$result
|
||||
.find(".list-count")
|
||||
.html(`<span>${__("Refreshing", null, "Document count in list view")}...</span>`);
|
||||
this.get_count_element().html(
|
||||
`<span>${__("Refreshing", null, "Document count in list view")}...</span>`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -616,11 +617,33 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
}
|
||||
|
||||
render_count() {
|
||||
if (!this.list_view_settings.disable_count) {
|
||||
this.get_count_str().then((str) => {
|
||||
this.$result.find(".list-count").html(`<span>${str}</span>`);
|
||||
});
|
||||
}
|
||||
if (this.list_view_settings.disable_count) return;
|
||||
|
||||
let me = this;
|
||||
let $count = this.get_count_element();
|
||||
this.get_count_str().then((count) => {
|
||||
$count.html(`<span>${count}</span>`);
|
||||
if (this.count_upper_bound) {
|
||||
$count.attr(
|
||||
"title",
|
||||
__(
|
||||
"The count shown is an estimated count. Click here to see the accurate count."
|
||||
)
|
||||
);
|
||||
$count.tooltip({ delay: { show: 600, hide: 100 }, trigger: "hover" });
|
||||
$count.on("click", () => {
|
||||
me.count_upper_bound = 0;
|
||||
$count.off("click");
|
||||
$count.tooltip("disable");
|
||||
me.freeze();
|
||||
me.render_count();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get_count_element() {
|
||||
return this.$result.find(".list-count");
|
||||
}
|
||||
|
||||
get_header_html() {
|
||||
|
|
@ -946,12 +969,21 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
return frappe.db
|
||||
.count(this.doctype, {
|
||||
filters: this.get_filters_for_args(),
|
||||
limit: this.count_upper_bound,
|
||||
})
|
||||
.then((total_count) => {
|
||||
this.total_count = total_count || current_count;
|
||||
this.count_without_children =
|
||||
count_without_children !== current_count ? count_without_children : undefined;
|
||||
let str = __("{0} of {1}", [current_count, this.total_count]);
|
||||
|
||||
let count_str;
|
||||
if (this.total_count === this.count_upper_bound) {
|
||||
count_str = `${format_number(this.total_count - 1, null, 0)}+`;
|
||||
} else {
|
||||
count_str = format_number(this.total_count, null, 0);
|
||||
}
|
||||
|
||||
let str = __("{0} of {1}", [format_number(current_count, null, 0), count_str]);
|
||||
if (this.count_without_children) {
|
||||
str = __("{0} of {1} ({2} rows with children)", [
|
||||
count_without_children,
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ frappe.views.FileView = class FileView extends frappe.views.ListView {
|
|||
} else {
|
||||
super.render();
|
||||
this.render_header();
|
||||
this.render_count();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -220,19 +220,14 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
this.setup_datatable(this.data);
|
||||
}
|
||||
|
||||
render_count() {
|
||||
if (this.list_view_settings?.disable_count) {
|
||||
return;
|
||||
}
|
||||
let $list_count = this.$paging_area.find(".list-count");
|
||||
if (!$list_count.length) {
|
||||
$list_count = $("<span>")
|
||||
get_count_element() {
|
||||
let $count = this.$paging_area.find(".list-count");
|
||||
if (!$count.length) {
|
||||
$count = $("<span>")
|
||||
.addClass("text-muted list-count")
|
||||
.prependTo(this.$paging_area.find(".level-right"));
|
||||
}
|
||||
this.get_count_str().then((str) => {
|
||||
$list_count.text(str);
|
||||
});
|
||||
return $count;
|
||||
}
|
||||
|
||||
on_update(data) {
|
||||
|
|
|
|||
|
|
@ -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