Commit ba7f3ed9 authored by Yuheng Huang's avatar Yuheng Huang Committed by Josip Sokcevic

Implement fuzzy search using fuse.js

Search for item.title and item.hostname using weighted search from
fuse.js. Scores are calculated from matching item.title and item.hostname.
Items are sorted by scores by desc so better matching items will show up
first. Also extract item.hostname from item.url.

Bug: 1099917
Change-Id: Ieaee008b778dda90ef08ed504da034080b71cc58
Reviewed-on: https://chrome-internal-review.googlesource.com/c/chrome/browser/resources/tab_search/+/3222931Reviewed-by: default avatarJohn Lee <johntlee@chromium.org>
Reviewed-by: default avatarTom Lukaszewicz <tluk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#819594}
parent 6d445e50
......@@ -12,6 +12,7 @@ js_type_check("closure_compile") {
is_polymer3 = true
deps = [
":app",
":fuzzy_search",
":tab_search_api_proxy",
":tab_search_item",
":tab_search_search_field",
......@@ -20,6 +21,7 @@ js_type_check("closure_compile") {
js_library("app") {
deps = [
":fuzzy_search",
":tab_search_api_proxy",
":tab_search_item",
":tab_search_search_field",
......@@ -30,6 +32,9 @@ js_library("app") {
externs_list = [ "$externs_path/metrics_private.js" ]
}
js_library("fuzzy_search") {
}
js_library("tab_search_api_proxy") {
deps = [
"//chrome/browser/ui/webui/tab_search:mojo_bindings_js_library_for_compile",
......
......@@ -13,8 +13,10 @@ import './tab_search_search_field.js'
import {assert} from 'chrome://resources/js/assert.m.js';
import {isMac} from 'chrome://resources/js/cr.m.js';
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {listenOnce} from 'chrome://resources/js/util.m.js';
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {fuzzySearch} from './fuzzy_search.js';
import {TabSearchApiProxy, TabSearchApiProxyImpl} from './tab_search_api_proxy.js';
export class TabSearchAppElement extends PolymerElement {
......@@ -278,19 +280,17 @@ export class TabSearchAppElement extends PolymerElement {
* @private
*/
updateFilteredTabs_(windowTabs) {
const lowerCaseSearchText = this.searchText_.toLowerCase();
this.filteredOpenTabs_ = !!this.searchText_ ?
windowTabs
.map(window => {
return window.tabs.filter(item => {
return item.title.toLowerCase().includes(lowerCaseSearchText);
});
})
.flat() :
windowTabs.map(window => window.tabs).flat();
this.filteredOpenTabs_.sort((a, b) => (b.lastActiveTimeTicks && a.lastActiveTimeTicks) ?
const result = [];
windowTabs.forEach(window => {
window.tabs.forEach(tab => {
result.push(
Object.assign({}, tab, {hostname: new URL(tab.url).hostname}));
});
});
result.sort((a, b) => (b.lastActiveTimeTicks && a.lastActiveTimeTicks) ?
b.lastActiveTimeTicks.internalValue - a.lastActiveTimeTicks.internalValue :
0);
this.filteredOpenTabs_ = fuzzySearch(this.searchText_, result);
}
}
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import Fuse from './fuse.js';
const OPTIONS = {
includeScore: true,
keys: [
{
name: 'title',
weight: 2,
},
{
name: 'hostname',
weight: 1,
}
]
};
/**
* @param {string} input
* @param {!Array<tabSearch.mojom.Tab>} records
* @return {!Array<tabSearch.mojom.Tab>}
*/
export function fuzzySearch(input, records) {
if (input.length === 0) {
return records;
}
return new Fuse(records, OPTIONS).search(input).map(_ => _.item);
}
......@@ -19,6 +19,9 @@
<include name="IDR_FUSE_JS"
file="../../../../third_party/fusejs/dist/fuse.basic.esm.min.js"
type="BINDATA"/>
<include name="IDR_FUZZY_SEARCH_JS"
file="fuzzy_search.js"
type="BINDATA"/>
<include name="IDR_TAB_SEARCH_API_PROXY_JS"
file="tab_search_api_proxy.js"
type="BINDATA" />
......
......@@ -11,7 +11,18 @@ js_type_check("closure_compile") {
"js_module_root=../../chrome/test/data/webui/",
"js_module_root=./gen/chrome/test/data/webui/",
]
deps = [ ":tab_search_app_test" ]
deps = [
":fuzzy_search_test",
":tab_search_app_test",
]
}
js_library("fuzzy_search_test") {
deps = [
":test_tab_search_api_proxy",
"../..:chai_assert",
]
externs_list = [ "$externs_path/mocha-2.5.js" ]
}
js_library("tab_search_app_test") {
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {fuzzySearch} from 'chrome://tab-search/fuzzy_search.js'
import {assertDeepEquals, assertEquals} from '../../chai_assert.js';
suite('FuzzySearchTest', () => {
test('fuzzySearch', () => {
const records = [
{
title: 'OpenGL',
hostname: 'https://www.opengl.org',
},
{
title: 'Google',
hostname: 'https://www.google.com',
},
];
assertDeepEquals(
[
records[1],
records[0],
],
fuzzySearch('gle', records));
assertDeepEquals(records, fuzzySearch('', records));
assertDeepEquals([], fuzzySearch('z', records));
});
});
......@@ -277,6 +277,7 @@ suite('TabSearchAppTest', () => {
(tabSearchApp.shadowRoot.querySelector('tab-search-item[id="1"]'));
assertEquals(updatedTab.title, tabSearchItem.data.title);
assertEquals(updatedTab.url, tabSearchItem.data.url);
assertEquals('example.com', tabSearchItem.data.hostname);
});
test('Verify initial tab render time is logged correctly', async () => {
......
......@@ -44,3 +44,15 @@ var TabSearchAppTest = class extends TabSearchBrowserTest {
TEST_F('TabSearchAppTest', 'All', function() {
mocha.run();
});
// eslint-disable-next-line no-var
var FuzzySearchTest = class extends TabSearchBrowserTest {
/** @override */
get browsePreload() {
return 'chrome://tab-search/test_loader.html?module=tab_search/test/fuzzy_search_test.js';
}
};
TEST_F('FuzzySearchTest', 'All', function() {
mocha.run();
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment