Skip to content
Snippets Groups Projects
Commit 8c271bb8 authored by Toufic Batache's avatar Toufic Batache
Browse files

Updated search to include linked and virtual variables

parent 95fc2c58
Branches
Tags 0.0.535
No related merge requests found
Pipeline #11340 passed
......@@ -12,7 +12,7 @@
import ShareLinkModal from "$lib/components/ShareLinkModal.svelte"
import { trackGlobalShare, trackSearchVariable } from "$lib/matomo"
import type { ParametricReform } from "$lib/reforms"
import NonVirtualVariablesSearchWorker from "$lib/search/search_worker_variables_non_virtual?worker"
import WithLinkedVariablesSearchWorker from "$lib/search/search_worker_variables_with_linked?worker"
import type { Situation } from "$lib/situations"
const displayModeWritable = getContext("displayMode") as Writable<
......@@ -44,7 +44,7 @@
const showTutorial = getContext("showTutorial") as Writable<boolean>
if (browser) {
searchWorker = new NonVirtualVariablesSearchWorker()
searchWorker = new WithLinkedVariablesSearchWorker()
}
if (searchWorker !== undefined) {
searchWorker.onmessage = function (event) {
......
......@@ -3,27 +3,25 @@
import type { DisplayMode } from "$lib/displays"
import { newSimulationUrl } from "$lib/urls"
import AllVariablesSearchWorker from "$lib/search/search_worker_variables_all?worker"
import NonVirtualVariablesSearchWorker from "$lib/search/search_worker_variables_non_virtual?worker"
import ParametersSearchWorker from "$lib/search/search_worker_parameters?worker"
import WithLinkedVariablesSearchWorker from "$lib/search/search_worker_variables_with_linked?worker"
import type { SearchResult } from "minisearch"
import { SearchMode } from "$lib/search/search_mode"
import { iterParameterAncestors } from "@openfisca/json-model"
import { createEventDispatcher } from "svelte"
import type { Parameter } from "@openfisca/json-model"
const resultHeight = 56
export let displayMode: DisplayMode
export let displayMode: DisplayMode | undefined = undefined
export let searchInputValue = ""
export let searchMode: SearchMode
export let resultsCountInDropdown = 5
// # Variables
export let showOnlyNonVirtualVariables = false
export let showWithLinkedVariables = false
// ! Variables
// # Parameters
export let dispatchItemClick = false
// ! Parameters
const dispatch = createEventDispatcher()
let isSearchInProgress = false
let pendingQuery: string | null = null
......@@ -33,8 +31,8 @@
if (browser) {
switch (searchMode) {
case SearchMode.variables:
searchWorker = showOnlyNonVirtualVariables
? new NonVirtualVariablesSearchWorker()
searchWorker = showWithLinkedVariables
? new WithLinkedVariablesSearchWorker()
: new AllVariablesSearchWorker()
break
case SearchMode.parameters:
......@@ -57,6 +55,16 @@
}
}
$: isSearchActive =
searchInputValue != undefined && searchInputValue.length > 0
$: if (isSearchActive) {
search(searchInputValue)
} else {
searchResults = []
}
$: dropdownMaxHeight = 8 + (resultsCountInDropdown + 0.5) * resultHeight
function search(query: string) {
if (searchWorker === undefined) return
......@@ -70,22 +78,6 @@
isSearchInProgress = true
searchWorker.postMessage(query)
}
let searchInputValue = ""
$: isSearchActive =
searchInputValue != undefined && searchInputValue.length > 0
$: isSearchActive ? search(searchInputValue) : (searchResults = [])
const dispatch = createEventDispatcher()
function parameterClicked(event: Event, parameter: Parameter) {
if (dispatchItemClick) {
event.preventDefault()
dispatch("itemClick", parameter)
}
}
$: dropdownMaxHeight = 8 + (resultsCountInDropdown + 0.5) * resultHeight
</script>
<label class="uppercase" for="search">
......@@ -96,7 +88,7 @@
{/if}
</label>
<div
class="shadow-inner-md mt-2 flex w-full items-center justify-between rounded-t-md border-b bg-gray-100 p-1 text-gray-400 focus-within:text-gray-900 hover:border-black"
class="px-2 shadow-inner-md mt-2 flex w-full items-center justify-between rounded-t-md border-b bg-gray-100 p-1 text-gray-400 focus-within:text-gray-900 hover:border-black"
style:box-shadow={isSearchActive ? "0 1px 6px rgba(32,33,36,.28)" : "none"}
>
<iconify-icon
......@@ -108,22 +100,25 @@
/>
<input
autocomplete="off"
class="w-full border-none bg-gray-100 text-base text-gray-900 placeholder-gray-400 !ring-transparent focus:outline-none"
class="w-full px-3 py-2 border-none bg-gray-100 text-base text-gray-900 placeholder-gray-400 !ring-transparent focus:outline-none"
id="search"
bind:value={searchInputValue}
placeholder="impôt sur le revenu, CSG, ..."
type="search"
/>
{#if isSearchActive}
<iconify-icon
<button
class="mx-1 cursor-pointer p-1 text-black"
on:click={() => (searchInputValue = "")}
>
<iconify-icon
class="block"
icon="ic-baseline-clear"
flip="horizontal"
width="22"
height="22"
on:click={() => (searchInputValue = "")}
on:keyup
/>
</button>
{/if}
</div>
......@@ -135,18 +130,18 @@
>
{#if searchResults.length > 0}
{#each searchResults as variable, index (`found_variable_$${index}`)}
<li>
<a
class="text-le-gris-dispositif-dark"
class="block px-8 py-4 text-le-gris-dispositif-dark hover:bg-gray-200"
href={newSimulationUrl({
...displayMode,
parameterName: undefined,
parametersVariableName: variable.name,
})}
>
<li class="px-8 py-4 hover:bg-gray-200">
{variable.label ?? variable.name}
</li>
</a>
</li>
{/each}
{:else}
<li class="px-8 py-4">Aucun résultat trouvé</li>
......@@ -243,18 +238,18 @@
>
{#if searchResults.length > 0}
{#each searchResults as parameter, index (`found_parameter_$${index}`)}
<li>
<a
class="text-le-gris-dispositif-dark"
on:click={(event) => parameterClicked(event, parameter)}
class="block px-8 py-4 text-le-gris-dispositif-dark hover:bg-gray-200"
on:click={() => dispatch("itemClick", parameter)}
href="/parameters/{parameter.name}"
>
<li class="px-8 py-4 hover:bg-gray-200">
{#each [...iterParameterAncestors(parameter)] as ancestor, index}
{#if index > 0}&gt;{/if}
<span class="mx-2 first:ml-0">{ancestor.title}</span>
{/each}
</li>
</a>
</li>
{/each}
{:else}
<li class="px-8 py-4">Aucun résultat trouvé</li>
......
......@@ -114,6 +114,12 @@ export const decompositionCoreByNameByReformName: {
export const waterfalls: Waterfall[] = waterfallsUnknown
export const withLinkedVariableNames = extractWithLinkedVariableNames(
decompositionCoreByName,
variableSummaryByName,
waterfalls,
)
export const nonVirtualVariablesName = extractNonVirtualVariablesName(
decompositionCoreByName,
variableSummaryByName,
......@@ -997,6 +1003,58 @@ function extractLinkedVariablesName(
}
}
function extractWithLinkedVariableNames(
decompositionCoreByName: DecompositionCoreByName,
variableSummaryByName: VariableByName,
waterfalls: Waterfall[],
) {
const linkedVariableNames = new Set<string>()
const variableNames: string[] = []
for (const { name: waterfallName, root } of waterfalls) {
for (const [name] of walkDecompositionsCore(
decompositionCoreByName,
waterfallName,
root,
true,
)) {
// Note: Duplicates are removed from variableNames, because a
// variable name may appear more than once in a decomposition.
if (!variableNames.includes(name)) {
variableNames.push(name)
const variableSummary = variableSummaryByName[name]
for (const linkedVariableName of variableSummary?.linked_added_variables ??
[]) {
linkedVariableNames.add(linkedVariableName)
}
for (const linkedVariableName of variableSummary?.linked_other_variables ??
[]) {
linkedVariableNames.add(linkedVariableName)
}
for (const linkedVariableName of variableSummary?.linked_output_variables ??
[]) {
linkedVariableNames.add(linkedVariableName)
}
}
}
}
// Add linked variables.
for (const linkedVariableName of linkedVariableNames) {
extractLinkedVariablesName(
linkedVariableNames,
linkedVariableName,
variableSummaryByName,
)
}
for (const linkedVariableName of linkedVariableNames) {
if (!variableNames.includes(linkedVariableName)) {
variableNames.push(linkedVariableName)
}
}
return variableNames
}
function extractNonVirtualVariablesName(
decompositionCoreByName: DecompositionCoreByName,
variableSummaryByName: VariableByName,
......
import MiniSearch from "minisearch"
import type { Variable } from "@openfisca/json-model"
import { nonVirtualVariablesName } from "$lib/decompositions"
import { variableSummaryByName } from "$lib/variables"
const idField = "name"
export const miniSearch = new MiniSearch({
fields: ["short_label", "label", "name"],
idField: idField,
idField: "name",
searchOptions: {
fuzzy: 0.3,
prefix: true,
......@@ -18,24 +12,3 @@ export const miniSearch = new MiniSearch({
},
},
})
export const allVariables: Variable[] = Object.values(variableSummaryByName)
const variablesById = allVariables.reduce(
(byId: { [id: string]: Variable }, variable: Variable) => {
byId[variable[idField]] = variable
return byId
},
{},
)
export const nonVirtualVariables = nonVirtualVariablesName
.map((item: string) => variablesById[item])
.filter((item) => item)
export const onMessage = function (event: MessageEvent) {
// The search string (aka the term) is in event.data.
const result = miniSearch.search(event.data).slice(0, 50)
const variableObjects = result.map((item) => variablesById[item.id])
postMessage(variableObjects)
}
export interface SearchVariable {
label: string
name: string
short_label: string
}
import {
miniSearch,
allVariables,
onMessage,
} from "$lib/search/search_common_variables"
import { miniSearch } from "$lib/search/search_common_variables"
import { variableSummaryByName } from "$lib/variables"
import type { SearchVariable } from "$lib/search/search_variable"
export const allVariables = Object.values(variableSummaryByName).map(
(variable) =>
({
label: variable.label,
name: variable.name,
short_label: variable.short_label,
}) as SearchVariable,
)
miniSearch.addAll(allVariables)
onmessage = onMessage
console.log(allVariables.length)
onmessage = function (event: MessageEvent) {
// The search string (aka the term) is in event.data.
const result = miniSearch.search(event.data).slice(0, 50)
const variableObjects = result.map((item) =>
allVariables.find((variable) => variable.name === item.id),
)
postMessage(variableObjects)
}
import {
miniSearch,
nonVirtualVariables,
onMessage,
} from "$lib/search/search_common_variables"
miniSearch.addAll(nonVirtualVariables)
onmessage = onMessage
import {
decompositionCoreByName,
withLinkedVariableNames,
} from "$lib/decompositions"
import { variableSummaryByName } from "$lib/variables"
import { miniSearch } from "$lib/search/search_common_variables"
import type { SearchVariable } from "$lib/search/search_variable"
export const withLinkedVariables = withLinkedVariableNames
.map(
(name: string) =>
variableSummaryByName[name] ?? { name, ...decompositionCoreByName[name] },
)
.map(
(variable) =>
({
label: variable.label,
name: variable.name,
short_label: variable.short_label,
}) as SearchVariable,
)
miniSearch.addAll(withLinkedVariables)
onmessage = function (event: MessageEvent) {
// The search string (aka the term) is in event.data.
const result = miniSearch.search(event.data).slice(0, 50)
const variableObjects = result.map((item) =>
withLinkedVariables.find((variable) => variable.name === item.id),
)
postMessage(variableObjects)
}
<script lang="ts">
import type { Parameter } from "@openfisca/json-model"
import { onMount } from "svelte"
import { browser } from "$app/environment"
import { goto } from "$app/navigation"
import { page } from "$app/stores"
import Search from "$lib/components/search/Search.svelte"
......@@ -10,17 +12,15 @@
export let data: PageData
let initialTerm: string | undefined = undefined
let searchInputValue = ""
$: url = $page.url
$: searchInputValue = url.searchParams.get("q") ?? ""
onMount(() => {
searchInputValue = url.searchParams.get("q") ?? ""
})
function searchTermChanged({ detail }: { detail: string }) {
if (initialTerm === undefined) {
initialTerm = searchInputValue
}
searchInputValue = detail
$: if (browser) {
history.replaceState(
null,
"",
......@@ -35,21 +35,6 @@
}: {
detail: Parameter
}) {
if (initialTerm !== undefined) {
// Restore the initial term in browser history.
await goto(
`${url.pathname}${
initialTerm ? `?q=${encodeURIComponent(initialTerm)}` : ""
}`,
{ replaceState: true },
)
// Push the current term.
await goto(
`${url.pathname}${
searchInputValue ? `?q=${encodeURIComponent(searchInputValue)}` : ""
}`,
)
}
// Go to parameter page.
await goto(`/parameters/${parameter.name}`)
}
......@@ -66,11 +51,9 @@
<div class="m-auto mt-4 w-4/5">
<Search
dispatchItemClick
searchMode={SearchMode.parameters}
on:change={searchTermChanged}
on:itemClick={parameterClicked}
{searchInputValue}
bind:searchInputValue
searchMode={SearchMode.parameters}
/>
</div>
</main>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment