Skip to content
Snippets Groups Projects
Commit 3cc7100c authored by Remi  PLANEL's avatar Remi PLANEL
Browse files

WIP for like gitlab filter UI

parent 32525729
No related branches found
No related tags found
2 merge requests!131Merge relevant Abstract and references,!123Resolve "Wizzard to create db filters"
Pipeline #116825 failed
<script setup lang="ts">
import type { FacetDistribution } from "meilisearch";
import { useDisplay } from "vuetify";
import { useFacetsStore, type Facets } from '~~/stores/facets'
interface SortItem {
key: string,
order: boolean | 'asc' | 'desc'
}
export interface Props {
title?: string
db?: string
sortBy?: SortItem[]
facets: string[]
headers: { title: string, key: string }[]
itemValue: string
}
export interface FilterItem {
type: 'facet' | 'operator' | 'value'
value: string
title: string
count?: number
}
const props = withDefaults(defineProps<Props>(), {
title: '',
db: 'refseq',
sortBy: () => [{ key: "type", order: "asc" }],
});
const sortByRef = ref(toValue(props.sortBy))
const facetsRef = toRef(() => props.facets)
const facetStore = useFacetsStore()
const search: Ref<string> = ref("");
const filter: Ref<string> = ref('')
const filterOrSearch: Ref<FilterItem[] | string | null> = ref(null)
const hitsPerPage: Ref<number> = ref(25)
const limit = ref(1000)
const filterError: Ref<string | null> = ref(null)
const page = ref(1)
const items = ref()
const facetDistribution: Ref<FacetDistribution | undefined> = ref()
const itemsLength = ref(0)
let pending = ref(false)
const { height } = useDisplay();
const minTableHeight = ref(400)
const computedTableHeight = computed(() => {
const computedHeight = height.value - 500
return computedHeight > minTableHeight.value ? computedHeight : minTableHeight.value
})
const msSortBy = computed(() => {
if (sortByRef.value.length > 0) {
return sortByRef.value.map((curr) => {
if (curr?.key && curr?.order) {
return `${curr.key}:${curr.order}`
}
else { return "" }
})
} else { return null }
})
const msFilter = computed(() => {
if (Array.isArray(filterOrSearch.value)) {
return filterOrSearch.value.map((it, index) => {
console.log(index, ' ', it)
if (index >= 1 && (index + 1) % 3 === 1) {
console.log("should add AND")
return ` AND ${it.value}`
} else { return it.value }
}).join("")
}
else { return "" }
})
onMounted(async () => {
searchOrFilter()
// const {
// hits,
// pending: p,
// totalHits,
// filterError: fe,
// facetDistribution: fd }
// = await useFetchMsDocument(
// "refseq",
// search,
// filter,
// limit,
// hitsPerPage,
// page,
// facetsRef,
// msSortBy
// )
// console.log("get the hits")
// console.log(hits)
// // console.log([...toValue(hits)])
// items.value = toValue(hits)
// itemsLength.value = toValue(totalHits)
// facetDistribution.value = toValue(fd)
// filterError.value = toValue(fe)
// pending.value = toValue(p)
})
// Fetch results
async function searchOrFilter() {
try {
pending.value = true
const {
hits,
totalHits,
// filterError: fe,
facetDistribution: fd }
= await useFetchMsDocument(
"refseq",
search,
msFilter,
limit,
hitsPerPage,
page,
facetsRef,
msSortBy
)
items.value = unref(hits)
itemsLength.value = toValue(totalHits)
facetDistribution.value = toValue(fd)
filterError.value = toValue(fe)
} catch (error: any) {
filterError.value = error
}
finally {
pending.value = false
}
}
watch(filterOrSearch, (fos) => {
if (Array.isArray(fos) && fos?.length % 3 === 0) {
searchOrFilter()
}
else { console.log("do not search") }
})
watch(page, () => {
searchOrFilter()
})
watch(facetDistribution, (facetDistri) => {
facetStore.setFacets({ facetDistribution: facetDistri, facetStat: undefined })
})
const filterStep = computed(() => {
return (Array.isArray(filterOrSearch.value) && filterOrSearch.value.length > 0) ? filterOrSearch.value?.length % 3 : null
})
const operatorItems = ref([
{ type: "operator", value: '=', title: "is" }, { type: "operator", value: '!=', title: "is not" }
])
const computedItems = computed(() => {
if (filterStep.value === null || filterStep.value === 0) {
return props.facets.map(value => {
return {
type: "facet",
value,
title: value
}
})
}
if (filterStep.value === 1) {
return operatorItems.value
}
if (filterStep.value === 2) {
// get the facet value
if (Array.isArray(filterOrSearch.value)) {
const { type, value } = filterOrSearch.value?.slice(-2, -1)[0]
//
// console.log(facetStore.facets?.facetDistribution[value])
return facetStore.facets?.facetDistribution?.[value] ? Object.entries(facetStore.facets.facetDistribution[value]).map(([key, val]) => {
return { type: "value", value: key, title: key, count: val }
}) : []
}
}
})
function selectItem(item) {
filterOrSearch.value = Array.isArray(filterOrSearch.value) ? [...filterOrSearch.value, item] : [item]
}
const autocompleteElem = ref()
</script>
<template>
<v-card flat>
<v-toolbar>
<!-- <v-toolbar-title>
{{ title }} ({{ itemsLength }})
</v-toolbar-title> -->
<v-autocomplete v-model:search="search" v-model:model-value="filterOrSearch" chips clearable
label="Search or filter results..." :items="computedItems" item-value="value" item-title="title" multiple
return-object append-inner-icon="md:search" @click:appendInner="searchOrFilter"
@click:clear="filterOrSearch = []">
<template #chip="{ props, item }">
<v-chip v-bind="props" :text="item.raw.title"></v-chip>
</template>
<template #item="{ props, item }">
<!-- <pre>{{ props["v-on"] }}</pre> -->
<!-- <pre v-for="(value, key) in props" :key="key">
<span v-if="key != 'ref'">{{ key }} - {{ value }}</span>
</pre> -->
<v-list-item v-bind="{ ...props, active: false, onClick: () => selectItem(item) }" :title="item.title"
:subtitle="item.raw?.count ? item.raw.count : ''" :value="props.value">
</v-list-item>
</template>
</v-autocomplete>
</v-toolbar>
<v-data-table-server v-model:page="page" v-model:items-per-page="hitsPerPage" v-model:sortBy="sortByRef"
fixed-header :loading="pending" :headers="headers" :items="items" :items-length="itemsLength"
:item-value="itemValue" multi-sort density="compact" :height="computedTableHeight" class="elevation-1 mt-2">
<template #[`item.accession_in_sys`]="{ item }">
<accession-chips :accessions="item.accession_in_sys" baseUrl="http://toto.pasteur.cloud"></accession-chips>
</template>
</v-data-table-server>
</v-card>
</template>
\ No newline at end of file
<script setup lang="ts">
const sortBy: Ref<{ key: string, order: string }[]> = ref([{ key: 'type', order: "asc" }])
const itemValue = ref("id");
const facets = ref([
"type",
"Superkingdom",
"phylum",
"order",
"family",
"genus",
"species",
])
const availableTaxo: Ref<string[]> = ref([
"species",
"genus",
"family",
"order",
"phylum",
"Superkingdom"
]);
const headers = ref([
{ title: "Replicon", key: "replicon" },
{
title: "Type",
key: "type",
},
{
title: "Subtype",
key: "subtype",
},
{
title: "Accessions",
key: "accession_in_sys"
}
])
const computedHeaders = computed(() => {
return [...headers.value, ...availableTaxo.value.map(taxo => {
return {
title: capitalize(taxo),
key: taxo
}
})]
})
function capitalize([first, ...rest]) {
return first.toUpperCase() + rest.join('').toLowerCase();
}
</script>
<template>
<ServerDbTable title="RefSeq" db="refseq" :sortBy="sortBy" :headers="computedHeaders" :item-value="itemValue" :facets="facets">
</ServerDbTable>
</template>
\ No newline at end of file
import { MeiliSearch } from 'meilisearch' import { MeiliSearch } from 'meilisearch'
import { useRuntimeConfig, watchEffect, type MaybeRef, ref, toValue } from '#imports' import { useRuntimeConfig, watchEffect, type MaybeRef, ref, toValue } from '#imports'
import type { FacetDistribution, Hits } from 'meilisearch'; import type { FacetDistribution, Hits } from 'meilisearch';
export function useFetchMsDocument( import { useAsyncState } from '@vueuse/core'
import { errorMonitor } from 'events';
export async function useFetchMsDocument(
index: MaybeRef<string> = ref(""), index: MaybeRef<string> = ref(""),
search: Ref<string> = ref(""), search: Ref<string> = ref(""),
filter: Ref<string> = ref(''), filter: Ref<string> = ref(''),
...@@ -18,12 +21,11 @@ export function useFetchMsDocument( ...@@ -18,12 +21,11 @@ export function useFetchMsDocument(
apiKey: runtimeConfig.public.meiliApiKey apiKey: runtimeConfig.public.meiliApiKey
}) })
const pending = ref(false) const pending = ref(false)
const filterError = ref(null) const filterError: Ref<string | null> = ref(null)
const hits: Ref<Hits<Record<string, any>>> = ref([]) const hits: Ref<Hits<Record<string, any>>> = ref([])
const totalHits = ref(0) const totalHits = ref(0)
const totalPages = ref(0) const totalPages = ref(0)
const facetDistribution: Ref<FacetDistribution | undefined> = ref({}) const facetDistribution: Ref<FacetDistribution | undefined> = ref({})
// reset page when filter and search change
watch(filter, () => { watch(filter, () => {
page.value = 1 page.value = 1
...@@ -32,34 +34,36 @@ export function useFetchMsDocument( ...@@ -32,34 +34,36 @@ export function useFetchMsDocument(
page.value = 1 page.value = 1
}) })
watchEffect(async () => {
try {
pending.value = true
const res = await client
.index(toValue(index))
.search(toValue(search), {
limit: toValue(limit),
filter: toValue(filter),
hitsPerPage: toValue(hitsPerPage),
page: toValue(page),
facets: toValue(facets),
sort: toValue(sort),
})
filterError.value = null
const { hits: resHits, totalHits: resTotalHits, totalPages: resTotalPages, facetDistribution: facetD } = res
totalHits.value = resTotalHits
hits.value = resHits
totalPages.value = resTotalPages
facetDistribution.value = facetD
} catch ({ code, message }) {
if (code === 'invalid_search_filter') {
filterError.value = message
}
} finally {
pending.value = false
}
})
try {
pending.value = true
console.log(pending.value)
const res = await client
.index(toValue(index))
.search(toValue(search), {
limit: toValue(limit),
filter: toValue(filter),
hitsPerPage: toValue(hitsPerPage),
page: toValue(page),
facets: toValue(facets),
sort: toValue(sort),
})
filterError.value = null
const { hits: resHits, totalHits: resTotalHits, totalPages: resTotalPages, facetDistribution: facetD } = res
totalHits.value = resTotalHits
hits.value = resHits
totalPages.value = resTotalPages
facetDistribution.value = facetD
pending.value = false
} catch (e: any) {
filterError.value = e
}
finally {
pending.value = false
}
// })
console.log(hits)
return { hits, totalHits, pending, filterError, totalPages, facetDistribution } return { hits, totalHits, pending, filterError, totalPages, facetDistribution }
} }
---
layout: db
navigation: false
---
# Refseq
Lorem markdownum arborea, euntem talia crimine parentque inque, belli nisi parte
induit; ut latos hortamine Aeneadae. Luctus madefacta o fluit ego exciderit
omnibus aestuat. Signum et se **illa vinci me** cortice Mopsopium pressum
*fessos*, inducere superabat liceat me. In et aethera mutavit, dictis sua, sub
insidiaeque, deus ramos illa hostem luet.
> Est inductaque sunt nec, *sua quam modumque*, peperisse nunc tantum. Quem
> natus non sed; aliquid *artus arvo* alter peragit? Labefecit marcida mirantes
> Numici memor laborem, mirae sequentis det ego borean defensae: innocuae. In
> rate dat verbis spuma saxo aquarum recipit exiguus exstant et quam, Peneiaque.
> **Altaria** ferus super vir.
Nulli venit pietas quodcumque oraque exemplo ut formae fugae hostibus iubenti,
avia quae. Virgae hominis in inutile; vagantur mortale est fero: sensit ne
felix.
1. Medicamen tulit dexteriore
2. Imagine veri bonis
3. Male sitim iacentes mittentis cepere dixit contigit
4. Pugman olivis tristique
5. Capere in vomentes cunctis
::refseq-db
::
## des choses à dire apres
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment