refactor string matching (#649)
This commit is contained in:
parent
38c26f8b5c
commit
2543bdcdfc
|
@ -49,6 +49,16 @@ export function parseWordsAsTags(text: string) {
|
||||||
return parseTags(taggedText)
|
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
|
// can't just do [StarterKit, Image...] because it doesn't work with cjs imports
|
||||||
export const exhibitExts = [
|
export const exhibitExts = [
|
||||||
Blockquote,
|
Blockquote,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Menu, Transition } from '@headlessui/react'
|
||||||
import { Avatar } from 'web/components/avatar'
|
import { Avatar } from 'web/components/avatar'
|
||||||
import { Row } from 'web/components/layout/row'
|
import { Row } from 'web/components/layout/row'
|
||||||
import { UserLink } from 'web/components/user-page'
|
import { UserLink } from 'web/components/user-page'
|
||||||
|
import { searchInAny } from 'common/util/parse'
|
||||||
|
|
||||||
export function FilterSelectUsers(props: {
|
export function FilterSelectUsers(props: {
|
||||||
setSelectedUsers: (users: User[]) => void
|
setSelectedUsers: (users: User[]) => void
|
||||||
|
@ -35,8 +36,7 @@ export function FilterSelectUsers(props: {
|
||||||
return (
|
return (
|
||||||
!selectedUsers.map((user) => user.name).includes(user.name) &&
|
!selectedUsers.map((user) => user.name).includes(user.name) &&
|
||||||
!ignoreUserIds.includes(user.id) &&
|
!ignoreUserIds.includes(user.id) &&
|
||||||
(user.name.toLowerCase().includes(query.toLowerCase()) ||
|
searchInAny(query, user.name, user.username)
|
||||||
user.username.toLowerCase().includes(query.toLowerCase()))
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { CreateGroupButton } from 'web/components/groups/create-group-button'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useMemberGroups } from 'web/hooks/use-group'
|
import { useMemberGroups } from 'web/hooks/use-group'
|
||||||
import { User } from 'common/user'
|
import { User } from 'common/user'
|
||||||
|
import { searchInAny } from 'common/util/parse'
|
||||||
|
|
||||||
export function GroupSelector(props: {
|
export function GroupSelector(props: {
|
||||||
selectedGroup?: Group
|
selectedGroup?: Group
|
||||||
|
@ -22,14 +23,10 @@ export function GroupSelector(props: {
|
||||||
const [isCreatingNewGroup, setIsCreatingNewGroup] = useState(false)
|
const [isCreatingNewGroup, setIsCreatingNewGroup] = useState(false)
|
||||||
|
|
||||||
const [query, setQuery] = useState('')
|
const [query, setQuery] = useState('')
|
||||||
const memberGroups = useMemberGroups(creator?.id)
|
const memberGroups = useMemberGroups(creator?.id) ?? []
|
||||||
const filteredGroups = memberGroups
|
const filteredGroups = memberGroups.filter((group) =>
|
||||||
? query === ''
|
searchInAny(query, group.name)
|
||||||
? memberGroups
|
)
|
||||||
: memberGroups.filter((group) => {
|
|
||||||
return group.name.toLowerCase().includes(query.toLowerCase())
|
|
||||||
})
|
|
||||||
: []
|
|
||||||
|
|
||||||
if (!showSelector || !creator) {
|
if (!showSelector || !creator) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -16,11 +16,6 @@ export type Sort =
|
||||||
| 'resolve-date'
|
| 'resolve-date'
|
||||||
| 'last-updated'
|
| '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() {
|
export function getSavedSort() {
|
||||||
// TODO: this obviously doesn't work with SSR, common sense would suggest
|
// 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
|
// that we should save things like this in cookies so the server has them
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { manaToUSD } from 'common/util/format'
|
||||||
import { quadraticMatches } from 'common/quadratic-funding'
|
import { quadraticMatches } from 'common/quadratic-funding'
|
||||||
import { Txn } from 'common/txn'
|
import { Txn } from 'common/txn'
|
||||||
import { useTracking } from 'web/hooks/use-tracking'
|
import { useTracking } from 'web/hooks/use-tracking'
|
||||||
|
import { searchInAny } from 'common/util/parse'
|
||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const txns = await getAllCharityTxns()
|
const txns = await getAllCharityTxns()
|
||||||
|
@ -88,10 +89,12 @@ export default function Charity(props: {
|
||||||
() =>
|
() =>
|
||||||
charities.filter(
|
charities.filter(
|
||||||
(charity) =>
|
(charity) =>
|
||||||
charity.name.toLowerCase().includes(query.toLowerCase()) ||
|
searchInAny(
|
||||||
charity.preview.toLowerCase().includes(query.toLowerCase()) ||
|
query,
|
||||||
charity.description.toLowerCase().includes(query.toLowerCase()) ||
|
charity.name,
|
||||||
(charity.tags as string[])?.includes(query.toLowerCase())
|
charity.preview,
|
||||||
|
charity.description
|
||||||
|
) || (charity.tags as string[])?.includes(query.toLowerCase())
|
||||||
),
|
),
|
||||||
[charities, query]
|
[charities, query]
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Answer } from 'common/answer'
|
import { Answer } from 'common/answer'
|
||||||
|
import { searchInAny } from 'common/util/parse'
|
||||||
import { sortBy } from 'lodash'
|
import { sortBy } from 'lodash'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { ContractsGrid } from 'web/components/contract/contracts-list'
|
import { ContractsGrid } from 'web/components/contract/contracts-list'
|
||||||
|
@ -28,22 +29,14 @@ export default function ContractSearchFirestore(props: {
|
||||||
const [sort, setSort] = useState(initialSort || 'newest')
|
const [sort, setSort] = useState(initialSort || 'newest')
|
||||||
const [query, setQuery] = useState(initialQuery)
|
const [query, setQuery] = useState(initialQuery)
|
||||||
|
|
||||||
const queryWords = query.toLowerCase().split(' ')
|
let matches = (contracts ?? []).filter((c) =>
|
||||||
function check(corpus: string) {
|
searchInAny(
|
||||||
return queryWords.every((word) => corpus.toLowerCase().includes(word))
|
query,
|
||||||
}
|
c.question,
|
||||||
|
c.creatorName,
|
||||||
let matches = (contracts ?? []).filter(
|
c.lowercaseTags.map((tag) => `#${tag}`).join(' '),
|
||||||
(c) =>
|
((c as any).answers ?? []).map((answer: Answer) => answer.text).join(' ')
|
||||||
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(' ')
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (sort === 'newest') {
|
if (sort === 'newest') {
|
||||||
|
|
|
@ -40,10 +40,7 @@ import React, { useEffect, useState } from 'react'
|
||||||
import { GroupChat } from 'web/components/groups/group-chat'
|
import { GroupChat } from 'web/components/groups/group-chat'
|
||||||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||||
import { Modal } from 'web/components/layout/modal'
|
import { Modal } from 'web/components/layout/modal'
|
||||||
import {
|
import { getSavedSort } from 'web/hooks/use-sort-and-query-params'
|
||||||
checkAgainstQuery,
|
|
||||||
getSavedSort,
|
|
||||||
} from 'web/hooks/use-sort-and-query-params'
|
|
||||||
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
|
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
|
||||||
import { toast } from 'react-hot-toast'
|
import { toast } from 'react-hot-toast'
|
||||||
import { useCommentsOnGroup } from 'web/hooks/use-comments'
|
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 { useTipTxns } from 'web/hooks/use-tip-txns'
|
||||||
import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
|
import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
|
||||||
import { OnlineUserList } from 'web/components/online-user-list'
|
import { OnlineUserList } from 'web/components/online-user-list'
|
||||||
|
import { searchInAny } from 'common/util/parse'
|
||||||
|
|
||||||
export const getStaticProps = fromPropz(getStaticPropz)
|
export const getStaticProps = fromPropz(getStaticPropz)
|
||||||
export async function getStaticPropz(props: { params: { slugs: string[] } }) {
|
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?
|
// TODO use find-active-contracts to sort by?
|
||||||
const matches = sortBy(members, [(member) => member.name]).filter(
|
const matches = sortBy(members, [(member) => member.name]).filter((m) =>
|
||||||
(m) =>
|
searchInAny(query, m.name, m.username)
|
||||||
checkAgainstQuery(query, m.name) || checkAgainstQuery(query, m.username)
|
|
||||||
)
|
)
|
||||||
const matchLimit = 25
|
const matchLimit = 25
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,12 @@ import { useUser } from 'web/hooks/use-user'
|
||||||
import { groupPath, listAllGroups } from 'web/lib/firebase/groups'
|
import { groupPath, listAllGroups } from 'web/lib/firebase/groups'
|
||||||
import { getUser, User } from 'web/lib/firebase/users'
|
import { getUser, User } from 'web/lib/firebase/users'
|
||||||
import { Tabs } from 'web/components/layout/tabs'
|
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 { SiteLink } from 'web/components/site-link'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { Avatar } from 'web/components/avatar'
|
import { Avatar } from 'web/components/avatar'
|
||||||
import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
|
import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button'
|
||||||
import { UserLink } from 'web/components/user-page'
|
import { UserLink } from 'web/components/user-page'
|
||||||
|
import { searchInAny } from 'common/util/parse'
|
||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const groups = await listAllGroups().catch((_) => [])
|
const groups = await listAllGroups().catch((_) => [])
|
||||||
|
@ -71,11 +71,13 @@ export default function Groups(props: {
|
||||||
const matches = sortBy(groups, [
|
const matches = sortBy(groups, [
|
||||||
(group) => -1 * group.contractIds.length,
|
(group) => -1 * group.contractIds.length,
|
||||||
(group) => -1 * group.memberIds.length,
|
(group) => -1 * group.memberIds.length,
|
||||||
]).filter(
|
]).filter((g) =>
|
||||||
(g) =>
|
searchInAny(
|
||||||
checkAgainstQuery(query, g.name) ||
|
query,
|
||||||
checkAgainstQuery(query, g.about || '') ||
|
g.name,
|
||||||
checkAgainstQuery(query, creatorsDict[g.creatorId].username)
|
g.about || '',
|
||||||
|
creatorsDict[g.creatorId].username
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const matchesOrderedByRecentActivity = sortBy(groups, [
|
const matchesOrderedByRecentActivity = sortBy(groups, [
|
||||||
|
@ -84,11 +86,13 @@ export default function Groups(props: {
|
||||||
(group.mostRecentChatActivityTime ??
|
(group.mostRecentChatActivityTime ??
|
||||||
group.mostRecentContractAddedTime ??
|
group.mostRecentContractAddedTime ??
|
||||||
group.mostRecentActivityTime),
|
group.mostRecentActivityTime),
|
||||||
]).filter(
|
]).filter((g) =>
|
||||||
(g) =>
|
searchInAny(
|
||||||
checkAgainstQuery(query, g.name) ||
|
query,
|
||||||
checkAgainstQuery(query, g.about || '') ||
|
g.name,
|
||||||
checkAgainstQuery(query, creatorsDict[g.creatorId].username)
|
g.about || '',
|
||||||
|
creatorsDict[g.creatorId].username
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Not strictly necessary, but makes the "hold delete" experience less laggy
|
// Not strictly necessary, but makes the "hold delete" experience less laggy
|
||||||
|
|
Loading…
Reference in New Issue
Block a user