Skip to content

Commit

Permalink
Optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
frostyfan109 committed Jul 31, 2024
1 parent 4c553f5 commit 3bc5839
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 50 deletions.
4 changes: 3 additions & 1 deletion src/components/search/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,8 +419,10 @@ export const HelxSearch = ({ children }) => {

study.elements.forEach((variable, indexByVariable) => {
const variableToUpdate = Object.assign({}, variable);
// NOTE: We don't want to store the actual study inside here, since the histogram
// will try to do a deep clone on it, which can become very performance heavy for large searches.
variableToUpdate["study_id"] = study.c_id
variableToUpdate["study_name"] = study.c_name
variableToUpdate["study"] = study
variableToUpdate["withinFilter"] = "none"
variables.push(variableToUpdate)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,32 @@ const VariableViewHistogram = () => {
/>
), [variableHistogramConfig])

const sliderMarks = useMemo(() => {
return normalizedVariableResults.reduce<SliderBaseProps["marks"]>((acc, cur) => {
acc![cur.score] = {
label: cur.score,
style: {
display: "none"
}
}
return acc
}, {})
}, [normalizedVariableResults])

const slider = useMemo(() => (
<DebouncedRangeSlider
value={ scoreFilter }
onChange={ setScoreFilter }
min={ 0 }
max={ 100 }
step={ null }
marks={ sliderMarks }
// Margin to align with the histogram
style={{ marginRight: 0, marginBottom: 4, marginTop: 16, flexGrow: 1 }}
className="histogram-slider"
/>
), [scoreFilter, setScoreFilter, sliderMarks])

return (
<Collapse
ghost
Expand Down Expand Up @@ -124,25 +150,7 @@ const VariableViewHistogram = () => {
<div style={{ display: "flex" }}>
<div style={{ flexGrow: 1, width: 0 }}>
{ histogram }
<DebouncedRangeSlider
value={ scoreFilter }
onChange={ setScoreFilter }
min={ Math.min(...normalizedVariableResults.map((result) => result.score)) }
max={ Math.max(...normalizedVariableResults.map((result) => result.score)) }
step={ null }
marks={ normalizedVariableResults.reduce<SliderBaseProps["marks"]>((acc, cur) => ({
...acc,
[cur.score]: {
label: cur.score,
style: {
display: "none"
}
}
}), {}) }
// Margin to align with the histogram
style={{ marginRight: 0, marginBottom: 4, marginTop: 16, flexGrow: 1 }}
className="histogram-slider"
/>
{ slider }
</div>
<HistogramLegend
title="Score Legend"
Expand Down Expand Up @@ -455,7 +463,7 @@ const StudyListItem = ({ study }: StudyListItemProps) => {
}

const VariableListItem = ({ variable, showStudySource=true, showDataSource=true, style={}, ...props }: VariableListItemProps) => {
const { dataSources, highlightTokens } = useVariableView()!
const { dataSources, highlightTokens, getStudyById } = useVariableView()!

const [showMore, setShowMore] = useState<boolean>(false)

Expand Down Expand Up @@ -535,9 +543,9 @@ const VariableListItem = ({ variable, showStudySource=true, showDataSource=true,
{ showStudySource && (
<span style={{ display: "inline-flex", alignItems: "center", fontSize: 12, color: "rgba(0, 0, 0, 0.45)" }}>
Source:&nbsp;<i>
<Highlighter textToHighlight={ variable.study.c_name } />
<Highlighter textToHighlight={ variable.study_name } />
</i>&nbsp;
<StudyInfoTooltip study={ variable.study } />
<StudyInfoTooltip study={ getStudyById(variable.study_id)! } />
</span>
) }
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ export interface VariableResult {
name: string
description: string
score: number
study: StudyResult
e_link: string
study_id: string
study_name: string
data_source: string // e.g. "HEAL Studies" vs "Non-HEAL Studies"
}

Expand All @@ -77,6 +78,8 @@ export interface IVariableViewContext {
studiesSource: StudyResult[]
filteredStudies: StudyResult[]
absScoreRange: [number, number]
getVariableById: (id: string) => VariableResult | undefined
getStudyById: (id: string) => StudyResult | undefined

// Filters
scoreFilter: [number, number] | undefined
Expand Down Expand Up @@ -126,16 +129,17 @@ export const VariableViewProvider = ({ children }: VariableViewProviderProps) =>
const [sortOrderOption, setSortOrderOption] = useState<string>("descending")
const [collapseIntoVariables, setCollapseIntoVariables] = useState<boolean>(true)

const variableIdMap = useMemo<Map<string, VariableResult>>(() => {
const map = new Map()
variableResults.forEach((variable) => {
map.set(variable.id, variable)
const [variableIdMap, studyIdMap] = useMemo<[Map<string, VariableResult>, Map<string, StudyResult>]>(() => {
const variableMap = new Map()
const studyMap = new Map()
variableStudyResults.forEach((study) => {
studyMap.set(study.c_id, study)
study.elements.forEach((variable) => variableMap.set(variable.id, variable))
})
return map
}, [variableResults])
return [variableMap, studyMap]
}, [variableStudyResults])

const normalizedVariableResults = useMemo(() => {
console.log("rerender normalized")
const values = variableResults.map(result => result.score);
const min = Math.min(...values)
const max = Math.max(...values)
Expand Down Expand Up @@ -171,7 +175,7 @@ export const VariableViewProvider = ({ children }: VariableViewProviderProps) =>
// We want to maintain the ordering of the variables, so compute using the ordered variables source.
const studiesSource = useMemo<StudyResult[]>(() => {
return variablesSource.reduce<StudyResult[]>((acc, variable) => {
const { study } = variable
const study = studyIdMap.get(variable.study_id)!
const existingStudy = acc.find((s) => s.c_id === study.c_id)
if (existingStudy) existingStudy.elements.push(variable)
else acc.push({
Expand All @@ -180,34 +184,33 @@ export const VariableViewProvider = ({ children }: VariableViewProviderProps) =>
})
return acc
}, [])
}, [variablesSource])
}, [variablesSource, studyIdMap])

const variableDocs = useMemo(() => variablesSource.map((variable) => ({
id: variable.id,
name: variable.name,
description: variable.description,
study_id: variable.study.c_id,
study_name: variable.study.c_name
study_id: variable.study_id,
study_name: variable.study_name
})), [variablesSource])

const lunrConfig = useMemo(() => ({
docs: variableDocs,
index: {
ref: "id",
fields: ["id", "name", "description", "study_id", "study_name"]
fields: ["id", "name", "description"]
}
}), [variableDocs])

const { index, lexicalSearch } = useLunrSearch(lunrConfig)

const [filteredVariables, highlightTokens] = useMemo<[VariableResult[], string[]]>(() => {
console.log("rerender filtered")
const { hits, tokens } = lexicalSearch(subsearch)
const matchedVariables = hits.map(({ ref: id }) => variableIdMap.get(id)!)
const matchedVariables = hits.reduce((acc, { ref: id }) => (acc.add(id), acc), new Set())
const highlightTokens = subsearch.length > 3 ? tokens.map((token) => token.toString()) : []

const filtered = [...variablesSource].filter((variable) => {
if (subsearch.length > 3 && !matchedVariables.includes(variable)) return false
const filtered = variablesSource.filter((variable) => {
if (subsearch.length > 3 && !matchedVariables.has(variable.id)) return false
if (scoreFilter) {
const [minScore, maxScore] = scoreFilter
if (variable.score < minScore || variable.score > maxScore) return false
Expand All @@ -216,10 +219,10 @@ export const VariableViewProvider = ({ children }: VariableViewProviderProps) =>
return true
})
return [filtered, highlightTokens]
}, [variablesSource, variableIdMap, scoreFilter, subsearch, lexicalSearch, hiddenDataSources])
}, [variablesSource, scoreFilter, subsearch, lexicalSearch, hiddenDataSources])

const filteredStudies = useMemo<StudyResult[]>(() => {
return [...studiesSource].reduce<StudyResult[]>((acc, study) => {
return studiesSource.reduce<StudyResult[]>((acc, study) => {
const newStudy = {
...study,
elements: study.elements.filter((variable) => filteredVariables.includes(variable))
Expand All @@ -230,7 +233,6 @@ export const VariableViewProvider = ({ children }: VariableViewProviderProps) =>
}, [studiesSource, filteredVariables])

const absScoreRange = useMemo<[number, number]>(() => {
console.log("rerender abs range")
if (normalizedVariableResults.length < 2) return [normalizedVariableResults[0]?.score, normalizedVariableResults[0]?.score]
return [
Math.min(...normalizedVariableResults.map((result) => result.score)),
Expand All @@ -250,7 +252,6 @@ export const VariableViewProvider = ({ children }: VariableViewProviderProps) =>
const variablesHistogram = useRef<ChartRef>()
const variableHistogramConfig = useMemo<G2ColumnConfig>(() => {
const [minScore, maxScore] = absScoreRange
console.log("RERENDER")
return {
...(variableHistogramConfigStatic as any),
data: [...filteredVariables].sort((a, b) => a.score - b.score),
Expand Down Expand Up @@ -315,12 +316,12 @@ export const VariableViewProvider = ({ children }: VariableViewProviderProps) =>
if (acc.hasOwnProperty(dataSource)) {
const { studies, variables } = acc[dataSource]
if (!variables.includes(cur.id)) variables.push(cur.id)
if (!studies.includes(cur.study.c_id)) studies.push(cur.study.c_id)
if (!studies.includes(cur.study_id)) studies.push(cur.study_id)
} else {
acc[dataSource] = {
name: dataSource,
color: FIXED_DATA_SOURCES[dataSource] ?? seededPalette.getNextColor(),
studies: [cur.study.c_id],
studies: [cur.study_id],
variables: [cur.id]
}
}
Expand Down Expand Up @@ -363,6 +364,9 @@ export const VariableViewProvider = ({ children }: VariableViewProviderProps) =>
setSortOrderOption("descending")
}, [normalizedVariableResults])

const getStudyById = useCallback((id: string) => studyIdMap.get(id), [studyIdMap])
const getVariableById = useCallback((id: string) => variableIdMap.get(id), [variableIdMap])

useEffect(() => {
resetFilters()
}, [normalizedVariableResults, resetFilters])
Expand Down Expand Up @@ -402,6 +406,7 @@ export const VariableViewProvider = ({ children }: VariableViewProviderProps) =>
studiesSource,
filteredVariables,
filteredStudies,
getStudyById, getVariableById,
/**
* Filters
*/
Expand Down
14 changes: 11 additions & 3 deletions src/hooks/use-lunr-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
*/

import { useCallback, useEffect } from 'react'
import { useCallback, useEffect, useMemo } from 'react'
import { useLunr } from './'

export const useLunrSearch = ({
Expand Down Expand Up @@ -39,13 +39,21 @@ export const useLunrSearch = ({
initIndex,
populateIndex
)

const docMap = useMemo(() => {
const map = new Map()
docs.forEach((doc) => map.set(doc[ref], doc))
return map
}, [docs, ref])

const getDocByRef = useCallback((ref) => docMap.get(ref), [docMap])

const lexicalSearch = useCallback((...args) => {
const result = lunrLexicalSearch(...args)
const searchTokens = []
result.forEach(({ ref: id, score, matchData: { metadata } }) => {
// `ref` is always a string, so the doc ref needs to be converted to a string.
const doc = docs.find((doc) => doc[ref].toString() === id)
const doc = getDocByRef(id)
Object.entries(metadata).forEach(([partialTerm, hitFields]) => {
Object.entries(hitFields).forEach(([ field, meta ]) => {
const { position: [[start, length]] } = meta
Expand All @@ -56,7 +64,7 @@ export const useLunrSearch = ({
})
})
return { hits: result, tokens: searchTokens }
}, [docs, lunrLexicalSearch])
}, [docs, getDocByRef, lunrLexicalSearch])

return {
index, lexicalSearch
Expand Down

0 comments on commit 3bc5839

Please sign in to comment.