Added auto complete provider utility functions
Summary: These are a series of utility functions for the auto complete system in the nav bar. Essentially, the auto complete system will gather information from Providers. Providers provide auto complete information from various sources i.e. bookmarks, recently viewed, or uri match patterns from the build. There are two main functions in this commit. The first is to turn the bookmarks Map into a Provider (bookmarksToAutoCompleteProvider). This runs in O(n) time where n is the number of bookmarks. A provider has an associated icon and matchPatterns to match what the user types to a uri. Here I concatenate the commonName and the uri of the bookmark together for the match pattern so that the user can search for both. filterProvidersToLineItems takes an Array of providers and returns line items. These are objects that will be displayed by the nav bar auto complete system. This has a little bit of a longer running time. O(mnop). Where m is the number of providers, n is the number of items in the largest provider, o is the length of the query and p is the length of the line items. This may seem bad, but I've tested the performance using 364 entries and it completes the function in less than a millisecond. The Providers will have precedence in the final auto complete box. With the most recent provder taking the most precedence, followed by bookmarks, followed by match patterns. Reviewed By: danielbuechele Differential Revision: D16568500 fbshipit-source-id: 00b043d51051ee86b3dbe18564e6f582b19e5359
This commit is contained in:
committed by
Facebook Github Bot
parent
00e8f43e37
commit
7a55fbc8dd
112
src/plugins/navigation/__tests__/testAutoCompleteSearch.node.js
Normal file
112
src/plugins/navigation/__tests__/testAutoCompleteSearch.node.js
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
* @flow strict-local
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {filterMatchPatterns} from '../util/autoCompleteProvider';
|
||||||
|
|
||||||
|
import type {URI} from '../flow-types';
|
||||||
|
|
||||||
|
// choose all k length combinations from array
|
||||||
|
const stringCombination: (Array<string>, number) => Array<string> = (
|
||||||
|
patterns,
|
||||||
|
k,
|
||||||
|
) => {
|
||||||
|
const n = patterns.length;
|
||||||
|
const returnArr = new Array(0);
|
||||||
|
const args = new Array(k).fill(0).map((_, idx) => idx);
|
||||||
|
(function build(args) {
|
||||||
|
const pattern = args.map(i => patterns[i]).join('');
|
||||||
|
returnArr.push(pattern);
|
||||||
|
if (args[args.length - 1] < n - 1) {
|
||||||
|
for (let i = args.length - 1; i >= 0; i--) {
|
||||||
|
const newArgs = args.map((value, idx) =>
|
||||||
|
idx >= i ? value + 1 : value,
|
||||||
|
);
|
||||||
|
build(newArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(args);
|
||||||
|
return returnArr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a map of 364 pairs
|
||||||
|
const constructMatchPatterns: () => Map<string, URI> = () => {
|
||||||
|
const matchPatterns = new Map<string, URI>();
|
||||||
|
|
||||||
|
const NUM_PATERNS_PER_ENTRY = 3;
|
||||||
|
|
||||||
|
const patterns = [
|
||||||
|
'abcdefghijklmnopqrstuvwxy',
|
||||||
|
'ababababababababababababa',
|
||||||
|
'cdcdcdcdcdcdcdcdcdcdcdcdc',
|
||||||
|
'efefefefefefefefefefefefe',
|
||||||
|
'ghghghghghghghghghghghghg',
|
||||||
|
'ijijijijijijijijijijijiji',
|
||||||
|
'klklklklklklklklklklklklk',
|
||||||
|
'mnmnmnmnmnmnmnmnmnmnmnmnm',
|
||||||
|
'opopopopopopopopopopopopo',
|
||||||
|
'qrqrqrqrqrqrqrqrqrqrqrqrq',
|
||||||
|
'ststststststststststststs',
|
||||||
|
'uvuvuvuvuvuvuvuvuvuvuvuvu',
|
||||||
|
'wxwxwxwxwxwxwxwxwxwxwxwxw',
|
||||||
|
'yzyzyzyzyzyzyzyzyzyzyzyzy',
|
||||||
|
];
|
||||||
|
|
||||||
|
stringCombination(patterns, NUM_PATERNS_PER_ENTRY).forEach(pattern =>
|
||||||
|
matchPatterns.set(pattern, pattern),
|
||||||
|
);
|
||||||
|
|
||||||
|
return matchPatterns;
|
||||||
|
};
|
||||||
|
|
||||||
|
test('construct match patterns', () => {
|
||||||
|
const matchPatterns = constructMatchPatterns();
|
||||||
|
expect(matchPatterns.size).toBe(364);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('search for abcdefghijklmnopqrstuvwxy in matchPatterns', () => {
|
||||||
|
const matchPatterns = constructMatchPatterns();
|
||||||
|
const filteredMatchPatterns = filterMatchPatterns(
|
||||||
|
matchPatterns,
|
||||||
|
'abcdefghijklmnopqrstuvwxy',
|
||||||
|
Infinity,
|
||||||
|
);
|
||||||
|
// Fixing abcdefghijklmnopqrstuvwxy, we have 13C2 = 78 patterns that will match
|
||||||
|
expect(filteredMatchPatterns.size).toBe(78);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('search for ????? in matchPatterns', () => {
|
||||||
|
const matchPatterns = constructMatchPatterns();
|
||||||
|
const filteredMatchPatterns = filterMatchPatterns(
|
||||||
|
matchPatterns,
|
||||||
|
'?????',
|
||||||
|
Infinity,
|
||||||
|
);
|
||||||
|
// ????? Does not exist in our seach so should return 0
|
||||||
|
expect(filteredMatchPatterns.size).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('search for abcdefghijklmnopqrstuvwxyababababababababababababacdcdcdcdcdcdcdcdcdcdcdcdc in matchPatterns', () => {
|
||||||
|
const matchPatterns = constructMatchPatterns();
|
||||||
|
const filteredMatchPatterns = filterMatchPatterns(
|
||||||
|
matchPatterns,
|
||||||
|
'abcdefghijklmnopqrstuvwxyababababababababababababacdcdcdcdcdcdcdcdcdcdcdcdc',
|
||||||
|
Infinity,
|
||||||
|
);
|
||||||
|
// Should only appear once in our patterns
|
||||||
|
expect(filteredMatchPatterns.size).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('find first five occurences of abcdefghijklmnopqrstuvwxy', () => {
|
||||||
|
const matchPatterns = constructMatchPatterns();
|
||||||
|
const filteredMatchPatterns = filterMatchPatterns(
|
||||||
|
matchPatterns,
|
||||||
|
'abcdefghijklmnopqrstuvwxy',
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
expect(filteredMatchPatterns.size).toBe(5);
|
||||||
|
});
|
||||||
@@ -6,10 +6,12 @@
|
|||||||
* @flow strict-local
|
* @flow strict-local
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export type URI = string;
|
||||||
|
|
||||||
export type State = {|
|
export type State = {|
|
||||||
bookmarks: Map<string, Bookmark>,
|
bookmarks: Map<URI, Bookmark>,
|
||||||
shouldShowSaveBookmarkDialog: boolean,
|
shouldShowSaveBookmarkDialog: boolean,
|
||||||
saveBookmarkURI: ?string,
|
saveBookmarkURI: ?URI,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
export type PersistedState = {|
|
export type PersistedState = {|
|
||||||
@@ -18,10 +20,21 @@ export type PersistedState = {|
|
|||||||
|
|
||||||
export type NavigationEvent = {|
|
export type NavigationEvent = {|
|
||||||
date: ?Date,
|
date: ?Date,
|
||||||
uri: ?string,
|
uri: ?URI,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
export type Bookmark = {|
|
export type Bookmark = {|
|
||||||
uri: string,
|
uri: URI,
|
||||||
commonName: string,
|
commonName: string,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
|
export type AutoCompleteProvider = {|
|
||||||
|
icon: string,
|
||||||
|
matchPatterns: Map<string, URI>,
|
||||||
|
|};
|
||||||
|
|
||||||
|
export type AutoCompleteLineItem = {|
|
||||||
|
icon: string,
|
||||||
|
matchPattern: string,
|
||||||
|
uri: URI,
|
||||||
|
|};
|
||||||
|
|||||||
76
src/plugins/navigation/util/autoCompleteProvider.js
Normal file
76
src/plugins/navigation/util/autoCompleteProvider.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
* @flow strict-local
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {
|
||||||
|
URI,
|
||||||
|
Bookmark,
|
||||||
|
AutoCompleteProvider,
|
||||||
|
AutoCompleteLineItem,
|
||||||
|
} from '../flow-types';
|
||||||
|
|
||||||
|
export const bookmarksToAutoCompleteProvider: (
|
||||||
|
Map<URI, Bookmark>,
|
||||||
|
) => AutoCompleteProvider = bookmarks => {
|
||||||
|
const autoCompleteProvider = {
|
||||||
|
icon: 'bookmark',
|
||||||
|
matchPatterns: new Map<string, URI>(),
|
||||||
|
};
|
||||||
|
bookmarks.forEach((bookmark, uri) => {
|
||||||
|
const matchPattern = bookmark.commonName + ' - ' + uri;
|
||||||
|
autoCompleteProvider.matchPatterns.set(matchPattern, uri);
|
||||||
|
});
|
||||||
|
return autoCompleteProvider;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filterMatchPatterns: (
|
||||||
|
Map<string, URI>,
|
||||||
|
string,
|
||||||
|
number,
|
||||||
|
) => Map<string, URI> = (matchPatterns, query, maxItems) => {
|
||||||
|
const filteredPatterns = new Map<string, URI>();
|
||||||
|
for (const [pattern, uri] of matchPatterns) {
|
||||||
|
if (filteredPatterns.size >= maxItems) {
|
||||||
|
break;
|
||||||
|
} else if (pattern.toLowerCase().includes(query.toLowerCase())) {
|
||||||
|
filteredPatterns.set(pattern, uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredPatterns;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterProvider: (
|
||||||
|
AutoCompleteProvider,
|
||||||
|
string,
|
||||||
|
number,
|
||||||
|
) => AutoCompleteProvider = (provider, query, maxItems) => {
|
||||||
|
return {
|
||||||
|
...provider,
|
||||||
|
matchPatterns: filterMatchPatterns(provider.matchPatterns, query, maxItems),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filterProvidersToLineItems: (
|
||||||
|
Array<AutoCompleteProvider>,
|
||||||
|
string,
|
||||||
|
number,
|
||||||
|
) => Array<AutoCompleteLineItem> = (providers, query, maxItems) => {
|
||||||
|
let itemsLeft = maxItems;
|
||||||
|
const lineItems = new Array<AutoCompleteLineItem>(0);
|
||||||
|
for (const provider of providers) {
|
||||||
|
const filteredProvider = filterProvider(provider, query, itemsLeft);
|
||||||
|
filteredProvider.matchPatterns.forEach((uri, matchPattern) => {
|
||||||
|
lineItems.unshift({
|
||||||
|
icon: provider.icon,
|
||||||
|
matchPattern,
|
||||||
|
uri,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
itemsLeft -= filteredProvider.matchPatterns.size;
|
||||||
|
}
|
||||||
|
return lineItems;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user