refactor string matching (#649)

This commit is contained in:
Sinclair Chen 2022-07-15 14:16:00 -07:00 committed by GitHub
parent 38c26f8b5c
commit 2543bdcdfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 52 additions and 53 deletions

View File

@ -49,6 +49,16 @@ export function parseWordsAsTags(text: string) {
return parseTags(taggedText)
}
// TODO: fuzzy matching
export const wordIn = (word: string, corpus: string) =>
corpus.toLocaleLowerCase().includes(word.toLocaleLowerCase())
const checkAgainstQuery = (query: string, corpus: string) =>
query.split(' ').every((word) => wordIn(word, corpus))
export const searchInAny = (query: string, ...fields: string[]) =>
fields.some((field) => checkAgainstQuery(query, field))
// can't just do [StarterKit, Image...] because it doesn't work with cjs imports
export const exhibitExts = [
Blockquote,

View File

@ -7,6 +7,7 @@ import { Menu, Transition } from '@headlessui/react'
import { Avatar } from 'web/components/avatar'
import { Row } from 'web/components/layout/row'
import { UserLink } from 'web/components/user-page'
import { searchInAny } from 'common/util/parse'
export function FilterSelectUsers(props: {
setSelectedUsers: (users: User[]) => void
@ -35,8 +36,7 @@ export function FilterSelectUsers(props: {
return (
!selectedUsers.map((user) => user.name).includes(user.name) &&
!ignoreUserIds.includes(user.id) &&
(user.name.toLowerCase().includes(query.toLowerCase()) ||
user.username.toLowerCase().includes(query.toLowerCase()))
searchInAny(query, user.name, user.username)
)
})
)

View File

@ -11,6 +11,7 @@ import { CreateGroupButton } from 'web/components/groups/create-group-button'
import { useState } from 'react'
import { useMemberGroups } from 'web/hooks/use-group'
import { User } from 'common/user'
import { searchInAny } from 'common/util/parse'
export function GroupSelector(props: {
selectedGroup?: Group
@ -22,14 +23,10 @@ export function GroupSelector(props: {
const [isCreatingNewGroup, setIsCreatingNewGroup] = useState(false)
const [query, setQuery] = useState('')
const memberGroups = useMemberGroups(creator?.id)
const filteredGroups = memberGroups
? query === ''
? memberGroups
: memberGroups.filter((group) => {
return group.name.toLowerCase().includes(query.toLowerCase())
})
: []
const memberGroups = useMemberGroups(creator?.id) ?? []
const filteredGroups = memberGroups.filter((group) =>
searchInAny(query, group.name)
)
if (!showSelector || !creator) {
return (

View File

@ -16,11 +16,6 @@ export type Sort =
| 'resolve-date'
| 'last-updated'
export function checkAgainstQuery(query: string, corpus: string) {
const queryWords = query.toLowerCase().split(' ')
return queryWords.every((word) => corpus.toLowerCase().includes(word))
}
export function getSavedSort() {
// TODO: this obviously doesn't work with SSR, common sense would suggest
// that we should save things like this in cookies so the server has them

View File

@ -20,6 +20,7 @@ import { manaToUSD } from 'common/util/format'
import { quadraticMatches } from 'common/quadratic-funding'
import { Txn } from 'common/txn'
import { useTracking } from 'web/hooks/use-tracking'
import { searchInAny } from 'common/util/parse'
export async function getStaticProps() {
const txns = await getAllCharityTxns()
@ -88,10 +89,12 @@ export default function Charity(props: {
() =>
charities.filter(
(charity) =>
charity.name.toLowerCase().includes(query.toLowerCase()) ||
charity.preview.toLowerCase().includes(query.toLowerCase()) ||
charity.description.toLowerCase().includes(query.toLowerCase()) ||
(charity.tags as string[])?.includes(query.toLowerCase())
searchInAny(
query,
charity.name,
charity.preview,
charity.description
) || (charity.tags as string[])?.includes(query.toLowerCase())
),
[charities, query]
)

View File

@ -1,4 +1,5 @@
import { Answer } from 'common/answer'
import { searchInAny } from 'common/util/parse'
import { sortBy } from 'lodash'
import { useState } from 'react'
import { ContractsGrid } from 'web/components/contract/contracts-list'
@ -28,22 +29,14 @@ export default function ContractSearchFirestore(props: {
const [sort, setSort] = useState(initialSort || 'newest')
const [query, setQuery] = useState(initialQuery)
const queryWords = query.toLowerCase().split(' ')
function check(corpus: string) {
return queryWords.every((word) => corpus.toLowerCase().includes(word))
}
let matches = (contracts ?? []).filter(
(c) =>
check(c.question) ||
check(c.creatorName) ||
check(c.creatorUsername) ||
check(c.lowercaseTags.map((tag) => `#${tag}`).join(' ')) ||
check(
((c as any).answers ?? [])
.map((answer: Answer) => answer.text)
.join(' ')
)
let matches = (contracts ?? []).filter((c) =>
searchInAny(
query,
c.question,
c.creatorName,
c.lowercaseTags.map((tag) => `#${tag}`).join(' '),
((c as any).answers ?? []).map((answer: Answer) => answer.text).join(' ')
)
)
if (sort === 'newest') {

View File

@ -40,10 +40,7 @@ import React, { useEffect, useState } from 'react'
import { GroupChat } from 'web/components/groups/group-chat'
import { LoadingIndicator } from 'web/components/loading-indicator'
import { Modal } from 'web/components/layout/modal'
import {
checkAgainstQuery,
getSavedSort,
} from 'web/hooks/use-sort-and-query-params'
import { getSavedSort } from 'web/hooks/use-sort-and-query-params'
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
import { toast } from 'react-hot-toast'
import { useCommentsOnGroup } from 'web/hooks/use-comments'
@ -56,6 +53,7 @@ import { SearchIcon } from '@heroicons/react/outline'
import { useTipTxns } from 'web/hooks/use-tip-txns'
import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
import { OnlineUserList } from 'web/components/online-user-list'
import { searchInAny } from 'common/util/parse'
export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz(props: { params: { slugs: string[] } }) {
@ -446,9 +444,8 @@ function GroupMemberSearch(props: { members: User[]; group: Group }) {
}
// TODO use find-active-contracts to sort by?
const matches = sortBy(members, [(member) => member.name]).filter(
(m) =>
checkAgainstQuery(query, m.name) || checkAgainstQuery(query, m.username)
const matches = sortBy(members, [(member) => member.name]).filter((m) =>
searchInAny(query, m.name, m.username)
)
const matchLimit = 25

View File

@ -12,12 +12,12 @@ import { useUser } from 'web/hooks/use-user'
import { groupPath, listAllGroups } from 'web/lib/firebase/groups'
import { getUser, User } from 'web/lib/firebase/users'
import { Tabs } from 'web/components/layout/tabs'
import { checkAgainstQuery } from 'web/hooks/use-sort-and-query-params'
import { SiteLink } from 'web/components/site-link'
import clsx from 'clsx'
import { Avatar } from 'web/components/avatar'
import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
import { UserLink } from 'web/components/user-page'
import { searchInAny } from 'common/util/parse'
export async function getStaticProps() {
const groups = await listAllGroups().catch((_) => [])
@ -71,11 +71,13 @@ export default function Groups(props: {
const matches = sortBy(groups, [
(group) => -1 * group.contractIds.length,
(group) => -1 * group.memberIds.length,
]).filter(
(g) =>
checkAgainstQuery(query, g.name) ||
checkAgainstQuery(query, g.about || '') ||
checkAgainstQuery(query, creatorsDict[g.creatorId].username)
]).filter((g) =>
searchInAny(
query,
g.name,
g.about || '',
creatorsDict[g.creatorId].username
)
)
const matchesOrderedByRecentActivity = sortBy(groups, [
@ -84,11 +86,13 @@ export default function Groups(props: {
(group.mostRecentChatActivityTime ??
group.mostRecentContractAddedTime ??
group.mostRecentActivityTime),
]).filter(
(g) =>
checkAgainstQuery(query, g.name) ||
checkAgainstQuery(query, g.about || '') ||
checkAgainstQuery(query, creatorsDict[g.creatorId].username)
]).filter((g) =>
searchInAny(
query,
g.name,
g.about || '',
creatorsDict[g.creatorId].username
)
)
// Not strictly necessary, but makes the "hold delete" experience less laggy