Skip to content
Snippets Groups Projects
Commit e08a5500 authored by Mathieu Massaviol's avatar Mathieu Massaviol
Browse files

WIP parameter genericity #29

parent 6c877d6e
No related branches found
No related tags found
1 merge request!75Resolve "Permettre la recherche d'un jeu de données"
import type { Meta, StoryObj } from '@storybook/vue3'
import OcResourceSearchFacetList from './OcResourceSearchFacetList.vue'
const meta: Meta<typeof OcResourceSearchFacetList> = {
component: OcResourceSearchFacetList
}
export default meta
type Story = StoryObj<typeof OcResourceSearchFacetList>
export const Default: Story = {
render: (args) => ({
components: { OcResourceSearchFacetList },
setup() {
return { args }
},
template: '<OcResourceSearchFacetList v-bind="args" />'
}),
args: {
modelValue: {}
}
}
<template>
<Panel :header="t('search.resourceType')" toggleable>
<template #toggleicon="toggleIconProps">
<i v-if="toggleIconProps.collapsed" class="fa-solid fa-chevron-down text-gray-700" />
<i v-else class="fa-solid fa-chevron-up text-gray-700" />
</template>
<div class="flex items-center gap-2" v-for="(dcatType, uri) in type2ResourceType" v-bind:key="uri">
<Checkbox
v-model="model.resourceType"
:value="uri"
:disabled="props.loading"
/>
<label for="resourceType" class="capitalize">
{{ t('resourceType.' + dcatType) }}
</label>
</div>
</Panel>
</template>
<script setup lang="ts">
import type { OcSearchQuery } from '@/declarations';
import { useI18n } from 'vue-i18n';
import Panel from 'primevue/panel'
import Checkbox from 'primevue/checkbox'
import { type2ResourceType } from '@/helpers/resourceType'
const { t } = useI18n()
const model = defineModel<Partial<OcSearchQuery>>({ required: true })
const props = defineProps({
loading: {
type: Boolean,
default: false
}
})
</script>
<style scoped>
:deep(.p-panel-header) {
@apply bg-primary rounded-t text-white mb-2;
}
:deep(.p-panel) {
@apply bg-gray-100;
}
</style>
\ No newline at end of file
...@@ -148,6 +148,21 @@ export const Default: Story = { ...@@ -148,6 +148,21 @@ export const Default: Story = {
'title': {"fr":"LabEx DRIIHM 2","en":"DRIIHM LabEx 2"}, 'title': {"fr":"LabEx DRIIHM 2","en":"DRIIHM LabEx 2"},
'graph': ["ex:sandbox2"] 'graph': ["ex:sandbox2"]
} }
},
community: {
description: {
fr: "Lauréat de la deuxième vague de l'appel à projet Laboratoire d'Excellence (LabEx) dans le cadre du programme « Investissements d'avenir », le LabEx DRIIHM, Dispositif de Recherche Interdisciplinaire sur les Interactions Hommes-Milieux, regroupe à ce jour 13 Observatoires Hommes-Milieux, outils d'observation de socio-écosystèmes impactés par un événement d'origine anthropique. Créés par le CNRS-INEE en 2007, ils sont répartis en France métropolitaine, en outre-mer et à l’étranger.",
en: "Laureate of the Laboratory for Excellence project (LabEx) in the program « Investment in the future », the DRIIHM LabEx, Device for Interdisciplinary Research on human-environments Interactions, aggregate 13 human-environments observatories (OHM in french), tools for observing socio-ecosystems impacted by anthropic events. Created by CNRS-INEE in 2007, they are located in metropolitan France, overseas France and abroad."
},
identifier: "189088ec-baa9-4397-8c6f-eefde9a3790c",
title: {
fr: "Communauté du LabEx DRIIHM",
en: "DRIIHM Community"
},
logo: "https://www.driihm.fr/images/images/logos_png/logo_DRIIHM_r%C3%A9duit.png",
name: "driihm",
isSpaceOf: "https://www.irit.fr/opencommon/agents/organization/9a20f121-c64e-4049-93a7-4bedbe819fd6",
color: 'linen'
} }
} }
} }
...@@ -51,9 +51,9 @@ import { iconsDict } from '@/helpers/icons'; ...@@ -51,9 +51,9 @@ import { iconsDict } from '@/helpers/icons';
import { getResourceTypeFromAtType, ResourceType } from '@/helpers/resourceType'; import { getResourceTypeFromAtType, ResourceType } from '@/helpers/resourceType';
import { computed, type PropType } from 'vue' import { computed, type PropType } from 'vue'
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import OcLink from '../OcLink.vue'; import OcLink from '@/components/OcLink.vue';
import { getResourceVisibility } from '@/helpers/resourceVisibility'; import { getResourceVisibility } from '@/helpers/resourceVisibility';
import OcVisibilityIcon from '../OcVisibilityIcon/OcVisibilityIcon.vue'; import OcVisibilityIcon from '@/components/OcVisibilityIcon/OcVisibilityIcon.vue';
const { t } = useI18n() const { t } = useI18n()
const { translateValue } = useTranslateValue() const { translateValue } = useTranslateValue()
......
...@@ -146,8 +146,14 @@ export type OcSearchResult = OcResource & { ...@@ -146,8 +146,14 @@ export type OcSearchResult = OcResource & {
/** A representation of a search query */ /** A representation of a search query */
export type OcSearchQuery = { export type OcSearchQuery = {
queryString?: string q?: string
resourceType?: Array<keyof typeof type2ResourceType> params?: Partial<Record<keyof typeof SearchQueryParams, string|string[]>>
}
export enum SearchQueryParams {
title = "http://purl.org/dc/terms/title",
description = "http://purl.org/dc/terms/description",
creator = "http://purl.org/dc/terms/creator"
} }
export type OcTreeNode = OcResource & { export type OcTreeNode = OcResource & {
......
<template> <template>
<OcLayoutSimple :breadcrumb-items="breadcrumbItems" :is-authenticated="accountStore.isAuthenticated"> <OcLayoutSimple :breadcrumb-items="breadcrumbItems" :is-authenticated="accountStore.isAuthenticated">
<div class="flex flex-row"> <div class="flex flex-row">
<div class="p-4 pl-8 w-2/12 ml-auto mr-auto">
<Panel :header="t('search.resourceType')" toggleable>
<template #toggleicon="toggleIconProps">
<i v-if="toggleIconProps.collapsed" class="fa-solid fa-chevron-down text-gray-700" />
<i v-else class="fa-solid fa-chevron-up text-gray-700" />
</template>
<div class="flex items-center gap-2" v-for="(dcatType, uri) in type2ResourceType" v-bind:key="uri">
<Checkbox
v-model="resourceType"
:inputId="uri"
name="resourceType"
:value="uri"
@change="submit"
:disabled="searching"
/>
<label for="resourceType" class="capitalize">
{{ t('resourceType.' + dcatType) }}
</label>
</div>
</Panel>
</div>
<div class="p-4 pl-8 w-9/12 ml-auto mr-auto"> <div class="p-4 pl-8 w-9/12 ml-auto mr-auto">
<h1 class="font-title text-4xl uppercase font-bold mb-4 text-primary"> <h1 class="font-title text-4xl uppercase font-bold mb-4 text-primary">
{{ t('search.title') }} {{ t('search.title') }}
...@@ -29,7 +8,7 @@ ...@@ -29,7 +8,7 @@
<IconField class="w-full mt-8 mb-4"> <IconField class="w-full mt-8 mb-4">
<InputText <InputText
id="search" id="search"
v-model="queryString" v-model="searchQuery.q"
fluid fluid
size="large" size="large"
:placeholder="t('community.homepage.searchBarPlaceholder')" :placeholder="t('community.homepage.searchBarPlaceholder')"
...@@ -75,7 +54,7 @@ ...@@ -75,7 +54,7 @@
import { useTranslateValue } from '@/composables/useTranslateValue' import { useTranslateValue } from '@/composables/useTranslateValue'
import { useAccountData } from '@/dataLoaders/account' import { useAccountData } from '@/dataLoaders/account'
import { useCommunityData } from '@/dataLoaders/community' import { useCommunityData } from '@/dataLoaders/community'
import type { OcBreadcrumbItem, OcSearchResult } from '@/declarations' import { SearchQueryParams, type OcBreadcrumbItem, type OcSearchQuery, type OcSearchResult } from '@/declarations'
import OcLayoutSimple from '@/layout/OcLayoutSimple/OcLayoutSimple.vue' import OcLayoutSimple from '@/layout/OcLayoutSimple/OcLayoutSimple.vue'
import { useAccountStore } from '@/stores/account' import { useAccountStore } from '@/stores/account'
import { computed, onBeforeMount, ref, watch } from 'vue' import { computed, onBeforeMount, ref, watch } from 'vue'
...@@ -83,13 +62,10 @@ import { useI18n } from 'vue-i18n' ...@@ -83,13 +62,10 @@ import { useI18n } from 'vue-i18n'
import IconField from 'primevue/iconfield' import IconField from 'primevue/iconfield'
import InputText from 'primevue/inputtext' import InputText from 'primevue/inputtext'
import InputIcon from 'primevue/inputicon' import InputIcon from 'primevue/inputicon'
import OcSearchResultCard from '@/components/OcSearchResultCard/OcSearchResultCard.vue' import OcSearchResultCard from '@/components/Search/OcSearchResultCard/OcSearchResultCard.vue'
import OcSearchResultCardSkeleton from '@/components/OcSearchResultCardSkeleton/OcSearchResultCardSkeleton.vue' import OcSearchResultCardSkeleton from '@/components/Search/OcSearchResultCardSkeleton/OcSearchResultCardSkeleton.vue'
import { getSearchResults, searchResources } from '@/sparql/search' import { getSearchResults, searchResources } from '@/sparql/search'
import Paginator from 'primevue/paginator' import Paginator from 'primevue/paginator'
import Panel from 'primevue/panel'
import Checkbox from 'primevue/checkbox'
import { type2ResourceType } from '@/helpers/resourceType'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
definePage({ definePage({
...@@ -113,8 +89,7 @@ const router = useRouter() ...@@ -113,8 +89,7 @@ const router = useRouter()
const itemsPerPage = 5 const itemsPerPage = 5
const start = ref(0) const start = ref(0)
const queryString = ref('') const searchQuery = ref<OcSearchQuery>({})
const resourceType = ref<string[]>([])
const searchResultList = ref<string[]>([]) const searchResultList = ref<string[]>([])
const searchResultObjects = ref<OcSearchResult[]>([]) const searchResultObjects = ref<OcSearchResult[]>([])
...@@ -131,8 +106,7 @@ const submit = () => { ...@@ -131,8 +106,7 @@ const submit = () => {
community: community.value.name community: community.value.name
}, },
query: { query: {
q: queryString.value, q: searchQuery.value.q,
type: resourceType.value,
start: start.value start: start.value
} }
}) })
...@@ -146,13 +120,7 @@ const search = async () => { ...@@ -146,13 +120,7 @@ const search = async () => {
searching.value = true searching.value = true
searchResultObjects.value = [] searchResultObjects.value = []
try { try {
searchResultList.value = await searchResources( searchResultList.value = await searchResources(searchQuery.value, accountStore.auth)
{
queryString: queryString.value,
resourceType: resourceType.value
},
accountStore.auth
)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
errorMessage.value = (e as Error).message ?? 'Error' errorMessage.value = (e as Error).message ?? 'Error'
...@@ -198,20 +166,24 @@ const breadcrumbItems = computed<OcBreadcrumbItem[]>(() => [ ...@@ -198,20 +166,24 @@ const breadcrumbItems = computed<OcBreadcrumbItem[]>(() => [
]) ])
const loadRouteParams = () => { const loadRouteParams = () => {
queryString.value = route.query.q as string ?? "" searchQuery.value.q = route.query.q as string ?? ""
if (Array.isArray(route.query.type)){ searchQuery.value.params = {}
resourceType.value = route.query.type as string[] for (const param in SearchQueryParams){
} else if (typeof route.query.type === 'string'){ if (Array.isArray(route.query[param])){
resourceType.value = [route.query.type as string] searchQuery.value.params[param] = route.query[param] as string[]
} else {
searchQuery.value.params[param] = route.query[param] as string
}
} }
start.value = parseInt(route.query.start as string) ?? 0 start.value = route.query.start ? parseInt(route.query.start as string) : 0
console.log(searchQuery.value)
} }
watch(() => [route.query.q, route.query.type], search) watch(() => route.query.q, search)
watch(() => route.query.page, () => { loadRouteParams(), getResultsForStart(start.value)}) watch(() => route.query.page, () => { getResultsForStart(start.value)})
onBeforeMount(() => { onBeforeMount(() => {
if (route.query.q || route.query.type) { if (Object.keys(route.query).length > 0) {
loadRouteParams() loadRouteParams()
search() search()
} }
...@@ -219,11 +191,4 @@ onBeforeMount(() => { ...@@ -219,11 +191,4 @@ onBeforeMount(() => {
</script> </script>
<style scoped> <style scoped>
:deep(.p-panel-header) {
@apply bg-primary rounded-t text-white mb-2;
}
:deep(.p-panel) {
@apply bg-gray-100;
}
</style> </style>
...@@ -7,16 +7,58 @@ import { resourceContext } from './resource' ...@@ -7,16 +7,58 @@ import { resourceContext } from './resource'
* Get results URI of a search query * Get results URI of a search query
*/ */
export const searchResources = async (query: OcSearchQuery, auth?: Credentials) => { export const searchResources = async (query: OcSearchQuery, auth?: Credentials) => {
let typeFilter = '' let queryFilter = ''
if (query.resourceType?.length) { if (query.q) {
typeFilter = 'FILTER (?type = <' queryFilter = `FILTER regex(?o, "${query.q}", "i")`
typeFilter += query.resourceType.join('> || ?type = <')
typeFilter += '>)'
} }
let queryFilter = '' if (query.params?.title){
if (query.queryString) { queryFilter += '?resource dct:title ?title.\n'
queryFilter = `FILTER regex(?o, "${query.queryString}", "i")` if (Array.isArray(query.params.title)){
query.params.title.forEach((title, index) => {
queryFilter += `
?resource dct:title ?title${index}.
FILTER regex(?title${index}, "${title}", "i")
`
})
} else {
queryFilter += `FILTER regex(?title, "${query.params.title}", "i")\n`
}
}
if (query.params?.description){
queryFilter += '?resource dct:description ?description.\n'
if (Array.isArray(query.params.description)){
query.params.description.forEach((description, index) => {
queryFilter += `
?resource dct:description ?description${index}.
FILTER regex(?description${index}, "${description}", "i")
`
})
} else {
queryFilter += `FILTER regex(?description, "${query.params.description}", "i")\n`
}
}
if (query.params?.creator){
queryFilter += `
?resource dct:creator ?creator.
VALUES ?pCreator { foaf:name foaf:givenName foaf:familyName foaf:firstName }
?creator ?pCreator ?oCreator.
`
if (Array.isArray(query.params.creator)){
query.params.creator.forEach((creator, index) => {
queryFilter += `
?resource dct:creator ?creator${index}.
VALUES ?pCreator${index} { foaf:name foaf:givenName foaf:familyName foaf:firstName }
?creator${index} ?pCreator${index} ?oCreator${index}.
FILTER regex(?oCreator${index}, "${creator}", "i")
`
})
} else {
queryFilter += `FILTER regex(?oCreator, "${query.params.creator}", "i")\n`
}
} }
const res = await executeSparqlSelect( const res = await executeSparqlSelect(
...@@ -25,12 +67,11 @@ export const searchResources = async (query: OcSearchQuery, auth?: Credentials) ...@@ -25,12 +67,11 @@ export const searchResources = async (query: OcSearchQuery, auth?: Credentials)
WHERE { WHERE {
?resource rdf:type ?type. ?resource rdf:type ?type.
?resource dct:identifier ?identifier. ?resource dct:identifier ?identifier.
${typeFilter}
FILTER EXISTS { VALUES ?p { dct:title dct:description }
VALUES ?p { dct:title dct:description } ?resource ?p ?o.
?resource ?p ?o ${queryFilter}
${queryFilter}
}
} ORDER BY ASC(?resource) } ORDER BY ASC(?resource)
`, `,
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment