Skip to content

Commit b097905

Browse files
committed
Filter: deduplicate annotations before filtering
If there are many spans of text for an annotation (many table cells, say), previously the filter would be applied once for each span, and matching annotations would appear n times in filter.annotations. In and of itself this is relatively harmless, but filterHighlights then maps back from annotations to highlights, and: for (var i = 0, len = filtered.length; i < len; i++) { highlights = highlights.not(filtered[i]._local.highlights); } is ruinous. This change dramatically improves filtering performance on my pet test case of big, overlapping highlights on a 5000×3 table. I have mixed feelings about using a new identifier in _local for this, but we don't have any guarantee that the id provided by the server is unique, or even present. I also have mixed feelings about setting the id in highlighter.js and using it in filter.js, but the same coupling exists for .data('annotation') already...
1 parent 4cbe49e commit b097905

File tree

3 files changed

+30
-8
lines changed

3 files changed

+30
-8
lines changed

src/ui/filter.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,14 @@ Filter.prototype.updateFilter = function (filter) {
160160
return;
161161
}
162162

163-
var annotations = this.highlights.map(function () {
164-
return $(this).data('annotation');
165-
});
166-
annotations = $.makeArray(annotations);
163+
var annotations = (function (highlights) {
164+
var annotationsById = {};
165+
highlights.each(function () {
166+
var a = $(this).data('annotation');
167+
annotationsById[a._local.id] = a;
168+
});
169+
return $.map(annotationsById, function(a) { return a; });
170+
}(this.highlights));
167171

168172
for (var i = 0, len = annotations.length; i < len; i++) {
169173
var annotation = annotations[i],

src/ui/highlighter.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ Highlighter.prototype.drawAll = function (annotations) {
119119
return p;
120120
};
121121

122+
// local unique IDs for annotations, used to deduplicate when recovering them
123+
// from highlights.
124+
var id = (function () {
125+
var counter = 0;
126+
return function() {
127+
return '_local_id_' + (++counter);
128+
}
129+
}());
130+
122131
// Public: Draw highlights for the annotation.
123132
//
124133
// annotation - An annotation Object for which to draw highlights.
@@ -144,6 +153,11 @@ Highlighter.prototype.draw = function (annotation) {
144153
if (!hasHighlights) {
145154
annotation._local.highlights = [];
146155
}
156+
var hasLocalId = (typeof annotation._local.id !== 'undefined' &&
157+
annotation._local.id === null);
158+
if (!hasLocalId) {
159+
annotation._local.id = id();
160+
}
147161

148162
for (var j = 0, jlen = normedRanges.length; j < jlen; j++) {
149163
var normed = normedRanges[j];

test/spec/ui/filter_spec.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,14 @@ describe('ui.filter.Filter', function () {
125125
};
126126
annotations = [{text: 'cat'}, {text: 'dog'}, {text: 'car'}];
127127
$.each(annotations, function(i, annotation) {
128-
$('<span class="annotator-hl">')
129-
.text(annotation)
130-
.data('annotation', annotation)
131-
.appendTo(element);
128+
this._local = {id: this.text};
129+
for (var j = 0; j < annotation.text.length; j++) {
130+
var ch = annotation.text[j]
131+
$('<span class="annotator-hl">')
132+
.text(ch)
133+
.data('annotation', annotation)
134+
.appendTo(element);
135+
}
132136
});
133137
plugin.filters = {'text': testFilter};
134138
plugin.highlights = $(element).find('.annotator-hl');

0 commit comments

Comments
 (0)