Skip to content
Snippets Groups Projects

Resolve "Wizzard to create db filters"

Merged Remi PLANEL requested to merge wizzard-db-filters into dev
Compare and Show latest version
1 file
+ 127
43
Compare changes
  • Side-by-side
  • Inline
+ 127
43
@@ -3,8 +3,6 @@
import { useDisplay } from "vuetify";
import { useFacetsStore, type Facets } from '~~/stores/facets'
import { useMeiliSearch } from "#imports"
import type { SearchResponse, SearchParams } from 'meilisearch'
interface SortItem {
key: string,
order: boolean | 'asc' | 'desc'
@@ -20,10 +18,16 @@ export interface Props {
}
export interface FilterItem {
type: 'facet' | 'operator' | 'value'
type: 'facet' | 'operator' | 'value' | 'text'
value: string
title: string
count?: number
props: {
[key: string]: any
// title: string
// value: any
}
// raw?: any
}
const props = withDefaults(defineProps<Props>(), {
@@ -39,10 +43,11 @@ const facetsRef = toRef(() => props.facets)
const { search: msSearch, result: msResult } = useMeiliSearch(props.db)
const facetStore = useFacetsStore()
const search: Ref<string> = ref("");
const filterOrSearch: Ref<FilterItem[] | string | null> = ref(null)
const filterOrSearch: Ref<FilterItem[] | null> = ref(null)
const hitsPerPage: Ref<number> = ref(25)
const limit = ref(1000)
const filterError: Ref<string | null> = ref(null)
const msFilter: Ref<string | undefined> = ref(undefined)
const page = ref(1)
let loading = ref(false)
@@ -53,8 +58,34 @@ const computedTableHeight = computed(() => {
return computedHeight > minTableHeight.value ? computedHeight : minTableHeight.value
})
const filterInputValues = computed(() => {
if (filterOrSearch.value != null) {
return filterOrSearch.value.filter(({ props }) => props.type !== 'text')
} else {
return null
}
})
const queryInputValue = computed(() => {
if (filterOrSearch.value !== null) {
const phrase = filterOrSearch.value
.filter((f) => {
console.log(f)
return f.props.type === 'text'
})
.map((f) => {
return f.value
})
.join(" ")
return `'${phrase}'`
} else {
return null
}
})
const isFilter = computed(() => {
return Array.isArray(filterOrSearch.value) && filterOrSearch.value.length % 3 === 0
return Array.isArray(filterOrSearch.value)
})
const msSortBy = computed(() => {
@@ -68,21 +99,6 @@ const msSortBy = computed(() => {
} else { return undefined }
})
const msFilter = computed(() => {
if (isFilter.value) {
return filterOrSearch.value.map((it, index) => {
if (index >= 1 && (index + 1) % 3 === 1) {
return ` AND ${it.value}`
} else if ((index + 1) % 3 === 0) {
return `"${it.value}"`
} else {
return `${it.value}`
}
}).join("")
}
else { return undefined }
})
const reactiveParams = reactive({
hitsPerPage: 25,
@@ -91,7 +107,7 @@ const reactiveParams = reactive({
facets: ["*"],
filter: [],
sort: ["type:asc"],
attributesToHighlight: ["*"]
// attributesToHighlight: ["*"]
})
@@ -115,7 +131,8 @@ const msError = computed(() => {
async function searchOrFilter() {
try {
loading.value = true
await msSearch(toValue(search), { ...reactiveParams, filter: msFilter.value, sort: msSortBy.value })
const q = queryInputValue.value === null ? "" : queryInputValue.value
await msSearch(q, { ...reactiveParams, filter: msFilter.value, sort: msSortBy.value })
} catch (error: any) {
filterError.value = error
console.log(error)
@@ -132,31 +149,62 @@ function clearFilterOrSearch() {
}
watch(msFilter, async (fos) => {
await searchOrFilter()
console.log("the filter change")
console.log(msFilter)
console.log(fos)
searchOrFilter()
search.value = ''
})
watch(msResult, (newRes) => {
console.log("facets disti changed !!!!!!!!!!!!!")
console.log(msResult)
console.log(newRes)
facetStore.setFacets({ facetDistribution: newRes.facetDistribution, facetStat: newRes.facetStat })
}, { deep: true })
watch(filterInputValues, (newSoF) => {
if (isFilter.value && filterInputValues.value !== null && filterInputValues.value?.length % 3 === 0) {
msFilter.value = filterInputValues.value.map((it, index) => {
if (index >= 1 && (index + 1) % 3 === 1) {
return ` AND ${it.value}`
} else if ((index + 1) % 3 === 0) {
return `"${it.value}"`
} else {
return `${it.value}`
}
}).join("")
}
})
const filterStep = computed(() => {
return (Array.isArray(filterOrSearch.value) && filterOrSearch.value.length > 0) ? filterOrSearch.value?.length % 3 : null
return filterInputValues.value !== null && filterInputValues.value.length > 0 ? filterInputValues.value?.length % 3 : null
})
const operatorItems = ref([
{ type: "operator", value: '=', title: "is" }, { type: "operator", value: '!=', title: "is not" }
{
type: "operator", value: '=', title: "is", props: {
type: "operator"
}
}, {
type: "operator", value: '!=', title: "is not", props: {
type: "operator"
}
}
])
const autocompleteItems = computed(() => {
if (filterStep.value === null || filterStep.value === 0) {
return props.facets.map(value => {
return {
type: "facet",
value,
title: value
title: value,
props: {
type: "facet"
}
}
})
}
@@ -167,55 +215,91 @@ const autocompleteItems = computed(() => {
// get the facet value
if (Array.isArray(filterOrSearch.value)) {
const { type, value } = filterOrSearch.value?.slice(-2, -1)[0]
return facetStore.facets?.facetDistribution?.[value] ? Object.entries(facetStore.facets.facetDistribution[value]).map(([key, val]) => {
return { type: "value", value: key, title: key, count: val }
console.log("compute new facets")
const facetDistri = facetStore.facets?.facetDistribution
console.log(facetDistri)
return facetDistri?.[value] ? Object.entries(facetDistri[value]).map(([key, val]) => {
return {
type: "value", value: key, title: key, count: val, props: {
type: 'value', count: val
}
}
}) : []
}
}
})
const canAddTextSearch = computed(() => {
if (filterOrSearch.value !== null && filterOrSearch.value.length > 0) {
const lastItem = filterOrSearch.value.slice(-1)[0]
return lastItem?.props.type === 'value' || lastItem?.props.type === "text"
}
return true
})
function selectItem(item) {
filterOrSearch.value = Array.isArray(filterOrSearch.value) ? [...filterOrSearch.value, item] : [item]
}
function deleteOneFilter(index) {
function deleteOneFilter(index: number) {
console.log("deleteOnefilter")
console.log(index)
console.log(isFilter.value)
console.log(filterOrSearch)
if (isFilter.value) {
filterOrSearch.value?.splice(index - 2, 2)
}
}
function clearSearch() {
search.value = ""
}
function runTextSearch() {
if (canAddTextSearch) {
const item: FilterItem = {
type: 'text', title: search.value, value: search.value, props: { type: "text" }
}
if (Array.isArray(filterOrSearch.value)) {
filterOrSearch.value = [
...filterOrSearch.value, item
]
} else {
filterOrSearch.value = [item]
}
search.value = ""
searchOrFilter()
}
}
</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" auto-select-first chips clearable
label="Search or filter results..." :items="autocompleteItems" item-value="value" item-title="title"
multiple return-object append-inner-icon="md:search" @click:appendInner="searchOrFilter"
@click:clear="clearFilterOrSearch" @update:modelValue="() => clearSearch()">
<v-autocomplete ref="autocompleteInput" v-model:search="search" v-model:model-value="filterOrSearch"
auto-select-first chips clearable label="Search or filter results..." :items="autocompleteItems"
item-value="value" item-title="title" multiple return-object append-inner-icon="md:search"
@click:appendInner="searchOrFilter" @click:clear="clearFilterOrSearch"
@update:modelValue="() => clearSearch()">
<template #chip="{ props, item, index }">
<v-chip v-if="(index + 1) % 3 === 0" v-bind="props" :text="item.raw.title" closable
@click:close="deleteOneFilter(index)"></v-chip>
<v-chip v-else 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>
<template #prepend-item>
<v-list-item v-if="canAddTextSearch" title="Text search" @click="runTextSearch"> </v-list-item>
</template>
</v-autocomplete>
</v-toolbar>
<v-data-table-server v-if="!msError" v-model:page="reactiveParams.page"