Take activeAt in account when using fuse.js index

This commit is contained in:
Fedor Indutny 2022-05-23 12:09:40 -07:00 committed by GitHub
parent 28ab6e11f6
commit 9f8ea5b202
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -5,12 +5,19 @@ import Fuse from 'fuse.js';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import { parseAndFormatPhoneNumber } from './libphonenumberInstance'; import { parseAndFormatPhoneNumber } from './libphonenumberInstance';
import { WEEK } from './durations';
// Fuse.js scores have order of 0.01
const ACTIVE_AT_SCORE_FACTOR = (1 / WEEK) * 0.01;
const FUSE_OPTIONS: Fuse.IFuseOptions<ConversationType> = { const FUSE_OPTIONS: Fuse.IFuseOptions<ConversationType> = {
// A small-but-nonzero threshold lets us match parts of E164s better, and makes the // A small-but-nonzero threshold lets us match parts of E164s better, and makes the
// search a little more forgiving. // search a little more forgiving.
threshold: 0.2, threshold: 0.2,
includeScore: true,
useExtendedSearch: true, useExtendedSearch: true,
// We sort manually anyway
shouldSort: true,
keys: [ keys: [
{ {
name: 'searchableTitle', name: 'searchableTitle',
@ -71,14 +78,14 @@ function searchConversations(
conversations: ReadonlyArray<ConversationType>, conversations: ReadonlyArray<ConversationType>,
searchTerm: string, searchTerm: string,
regionCode: string | undefined regionCode: string | undefined
): Array<ConversationType> { ): ReadonlyArray<Pick<Fuse.FuseResult<ConversationType>, 'item' | 'score'>> {
const maybeCommand = searchTerm.match(/^!([^\s]+):(.*)$/); const maybeCommand = searchTerm.match(/^!([^\s]+):(.*)$/);
if (maybeCommand) { if (maybeCommand) {
const [, commandName, query] = maybeCommand; const [, commandName, query] = maybeCommand;
const command = COMMANDS.get(commandName); const command = COMMANDS.get(commandName);
if (command) { if (command) {
return command(conversations, query); return command(conversations, query).map(item => ({ item }));
} }
} }
@ -98,8 +105,7 @@ function searchConversations(
cachedIndices.set(conversations, index); cachedIndices.set(conversations, index);
} }
const results = index.search(extendedSearchTerm); return index.search(extendedSearchTerm);
return results.map(result => result.item);
} }
export function filterAndSortConversationsByRecent( export function filterAndSortConversationsByRecent(
@ -108,7 +114,24 @@ export function filterAndSortConversationsByRecent(
regionCode: string | undefined regionCode: string | undefined
): Array<ConversationType> { ): Array<ConversationType> {
if (searchTerm.length) { if (searchTerm.length) {
return searchConversations(conversations, searchTerm, regionCode); const now = Date.now();
return searchConversations(conversations, searchTerm, regionCode)
.slice()
.sort((a, b) => {
const { activeAt: aActiveAt = 0 } = a.item;
const { activeAt: bActiveAt = 0 } = b.item;
// See: https://fusejs.io/api/options.html#includescore
// 0 score is a perfect match, 1 - complete mismatch
const aScore =
(now - aActiveAt) * ACTIVE_AT_SCORE_FACTOR + (a.score ?? 0);
const bScore =
(now - bActiveAt) * ACTIVE_AT_SCORE_FACTOR + (b.score ?? 0);
return aScore - bScore;
})
.map(result => result.item);
} }
return conversations.concat().sort((a, b) => { return conversations.concat().sort((a, b) => {
@ -126,7 +149,12 @@ export function filterAndSortConversationsByTitle(
regionCode: string | undefined regionCode: string | undefined
): Array<ConversationType> { ): Array<ConversationType> {
if (searchTerm.length) { if (searchTerm.length) {
return searchConversations(conversations, searchTerm, regionCode); return searchConversations(conversations, searchTerm, regionCode)
.slice()
.sort((a, b) => {
return (a.score ?? 0) - (b.score ?? 0);
})
.map(result => result.item);
} }
return conversations.concat().sort((a, b) => { return conversations.concat().sort((a, b) => {