Clustering dialog choices limit & performance improvements - fixes #695 (#2996)

* Clustering dialog choices limit & performance improvements - fixes #695

Fixes #695
- Caps the total number of choices displayed at 10,000 and warns when
  over the limit. Users can use facets to tune which clusters are displayed.
- Doubles the performance of the Javascript processing
- Only displays count of rows for a choice if it's > 1 to DOM elements
- Adds internationalization for row count

For 41K clusters containing 118K choices, processing dropped from
3m20s to 1m20s, but with the 10K choice cap total time is ~10sec.

* Restore even/odd row class

* Updates from review feedback
This commit is contained in:
Tom Morris 2020-08-01 04:59:41 -04:00 committed by GitHub
parent 5985f95ce1
commit 0ebd89c952
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 22 deletions

View File

@ -177,7 +177,9 @@
"core-dialogs/choice-avg-length": "Average Length of Choices",
"core-dialogs/choice-var-length": "Length Variance of Choices",
"core-dialogs/clusters-found": "<b>$1</b> {{plural:$1|cluster|clusters}} found",
"core-dialogs/clusters-filtered": "<b>$1</b> {{plural:$1|cluster|clusters}} filtered from <b>$2</b> total",
"core-dialogs/clusters-filtered": "<b>$1</b> {{plural:$1|cluster|clusters}} included from <b>$2</b> total",
"core-dialogs/cluster-row-limit-exceeded": "Exceeded limit of $1 total choices",
"core-dialogs/cluster-rows": "($1 rows)",
"core-dialogs/cluster-descr": "This feature helps you find groups of different cell values that might be alternative representations of the same thing. For example, the two strings \"New York\" and \"new york\" are very likely to refer to the same concept and just have capitalization differences, and \"Gödel\" and \"Godel\" probably refer to the same person.",
"core-dialogs/find-more": "Find out more…",
"core-dialogs/method": "Method&nbsp;",

View File

@ -165,6 +165,9 @@ ClusteringDialog.prototype._renderTable = function(clusters) {
var container = this._elmts.tableContainer;
if (clusters.length > 0) {
// TODO: This will never get rendered because we're blocking rendering
container.empty().html('<div>Processing clusters...</div>');
var table = $('<table></table>').addClass("clustering-dialog-entry-table")[0];
var trHead = table.insertRow(table.rows.length);
@ -175,13 +178,24 @@ ClusteringDialog.prototype._renderTable = function(clusters) {
$(trHead.insertCell(3)).text($.i18n('core-dialogs/merge'));
$(trHead.insertCell(4)).text($.i18n('core-dialogs/new-cell-val'));
var renderCluster = function(cluster) {
var tr = table.insertRow(table.rows.length);
tr.className = table.rows.length % 2 === 0 ? "odd" : "even";
var entryTemplate = document.createElement('a');
entryTemplate.href = "javascript:{}";
entryTemplate.title = $.i18n('core-dialogs/use-this-val');
$(tr.insertCell(0)).text(cluster.choices.length);
var browseLinkTemplate = $('<a target="_new" title="'+$.i18n('core-dialogs/browse-only-these')+'">'+$.i18n('core-dialogs/browse-this-cluster')+'</a>')
.addClass("clustering-dialog-browse-focus")
.css("visibility","hidden")
$(tr.insertCell(1)).text(cluster.rowCount);
var renderCluster = function(cluster, index) {
var tr = table.insertRow();
tr.className = index % 2 === 0 ? "odd" : "even"; // TODO: Unused?
var cell = tr.insertCell()
cell.textContent = cluster.choices.length.toString();
cell = tr.insertCell();
cell.textContent = cluster.rowCount.toString();
var facet = {
"c": {
@ -197,9 +211,8 @@ ClusteringDialog.prototype._renderTable = function(clusters) {
]
};
var ul = $('<ul></ul>');
var ul = document.createElement('ul');
var choices = cluster.choices;
var rowCount = 0;
var onClick = function() {
var parent = $(this).closest("tr");
var value = $(this).text();
@ -212,17 +225,21 @@ ClusteringDialog.prototype._renderTable = function(clusters) {
};
for (var c = 0; c < choices.length; c++) {
var choice = choices[c];
var li = $('<li></li>');
$('<a href="javascript:{}" title='+$.i18n('core-dialogs/use-this-val')+'></a>').text(choice.v).click(onClick).appendTo(li);
$('<span></span>').text("(" + choice.c + " rows)").addClass("clustering-dialog-entry-count").appendTo(li);
rowCount += choice.c;
var li = document.createElement('li');
var entry = entryTemplate.cloneNode();
entry.textContent = choice.v.toString();
entry.addEventListener('click', onClick);
li.append(entry);
if (choice.c > 1) {
$('<span></span>').text($.i18n("core-dialogs/cluster-rows", choice.c)).addClass("clustering-dialog-entry-count").appendTo(li);
}
facet.s[c] = {
"v": {
"v":choice.v,
"l":choice.v
}
};
li.appendTo(ul);
ul.append(li);
}
var params = [
@ -233,12 +250,11 @@ ClusteringDialog.prototype._renderTable = function(clusters) {
];
var url = "project?" + params.join("&");
var div = $('<div></div>').addClass("clustering-dialog-value-focus");
var div = document.createElement('div');
div.class = "clustering-dialog-value-focus";
var browseLink = $('<a target="_new" title="'+$.i18n('core-dialogs/browse-only-these')+'">'+$.i18n('core-dialogs/browse-this-cluster')+'</a>')
.addClass("clustering-dialog-browse-focus")
var browseLink = $(browseLinkTemplate).clone()
.attr("href",url)
.css("visibility","hidden")
.appendTo(div);
$(tr.insertCell(2))
@ -256,23 +272,29 @@ ClusteringDialog.prototype._renderTable = function(clusters) {
editCheck.attr("checked", "true");
}
var input = $('<input type="text" size="25" />')
$('<input type="text" size="25" />')
.attr("value", cluster.value)
.bind("keyup change input",function() {
cluster.value = this.value;
}).appendTo(tr.insertCell(4));
return choices.length;
};
for (var i = 0; i < clusters.length; i++) {
renderCluster(clusters[i]);
// TODO: Make this a preference "ui.clustering.choices.limit"
var maxRenderRows = 5000;
var totalRows = 0;
for (var clusterIndex = 0; clusterIndex < clusters.length && totalRows < maxRenderRows; clusterIndex++) {
totalRows += renderCluster(clusters[clusterIndex], clusterIndex);
}
container.empty().append(table);
this._elmts.resultSummary.html(
(clusters.length === this._clusters.length) ?
((totalRows >= maxRenderRows) ? $.i18n('core-dialogs/cluster-row-limit-exceeded', maxRenderRows) + '<br/> ' : '') +
((clusterIndex === this._clusters.length) ?
$.i18n('core-dialogs/clusters-found', this._clusters.length) :
$.i18n('core-dialogs/clusters-filtered', clusters.length, this._clusters.length)
$.i18n('core-dialogs/clusters-filtered', clusterIndex, this._clusters.length))
);
} else {