performance: Improve sort_recipients.

The sort_recipients helper is used for many different
typeaheads, such as compose PMs, compose mentions,
and some settings-related code.

We now avoid unnecessary sorting steps in cases
where we have plenty of results in the top buckets
(such as users who match on prefix).

This change should not have any user-facing
implications.
This commit is contained in:
Steve Howell 2020-01-10 14:06:20 +00:00 committed by Tim Abbott
parent 9830d0a4c7
commit 15e7f5828b
3 changed files with 51 additions and 9 deletions

View File

@ -32,6 +32,11 @@ const noop = function () {};
set_global('blueslip', {});
blueslip.warn = noop;
// Use a slightly larger value than what's user-facing
// to facilitate testing different combinations of
// broadcast-mentions/persons/groups.
ct.max_num_items = 10;
const emoji_stadium = {
name: 'stadium',
aliases: ['stadium'],

View File

@ -11,6 +11,10 @@ const autosize = require('autosize');
// highlighter that escapes (i.e. one that calls
// typeahead_helper.highlight_with_escaping).
// This is what we use for PM/compose typeaheads.
// We export it to allow tests to mock it.
exports.max_num_items = 5;
exports.emoji_collection = [];
exports.update_emoji_data = function () {
@ -511,7 +515,9 @@ exports.get_person_suggestions = function (query, opts) {
query,
compose_state.stream_name(),
compose_state.topic(),
filtered_groups);
filtered_groups,
exports.max_num_items
);
};
exports.get_sorted_filtered_items = function (query) {
@ -909,7 +915,7 @@ exports.initialize_compose_typeahead = function (selector) {
};
$(selector).typeahead({
items: 5,
items: exports.max_num_items,
dropup: true,
fixed: true,
// Performance note: We have trivial matcher/sorters to do
@ -1006,7 +1012,7 @@ exports.initialize = function () {
$("#private_message_recipient").typeahead({
source: exports.get_pm_people,
items: 5,
items: exports.max_num_items,
dropup: true,
fixed: true,
highlighter: function (item) {

View File

@ -311,11 +311,23 @@ exports.sort_languages = function (matches, query) {
return results.matches.concat(results.rest);
};
exports.sort_recipients = function (users, query, current_stream, current_topic, groups) {
exports.sort_recipients = function (
users,
query,
current_stream,
current_topic,
groups,
max_num_items
) {
if (!groups) {
groups = [];
}
if (max_num_items === undefined) {
max_num_items = 20;
}
function sort_relevance(items) {
return exports.sort_people_for_relevance(
items, current_stream, current_topic);
@ -357,11 +369,30 @@ exports.sort_recipients = function (users, query, current_stream, current_topic,
const worst_users = () => sort_relevance(email_results.rest);
const worst_groups = () => groups_results.rest;
return best_users().concat(
best_groups(),
ok_users(),
worst_users(),
worst_groups());
const getters = [
best_users,
best_groups,
ok_users,
worst_users,
worst_groups,
];
/*
The following optimization is important for large realms.
If we know we're only showing 5 suggestions, and we
get 5 matches from `best_users`, then we want to avoid
calling the expensives sorts for `ok_users` and `worst_users`,
since they just get dropped.
*/
let items = [];
_.each(getters, (getter) => {
if (items.length < max_num_items) {
items = items.concat(getter());
}
});
return items.slice(0, max_num_items);
};
function slash_command_comparator(slash_command_a, slash_command_b) {