From 1868ebc89efd6a8c1658a70fd73000235e6c1772 Mon Sep 17 00:00:00 2001 From: KevinRohlf <123676073+KevinRohlf@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:39:49 +0100 Subject: [PATCH 1/6] feat: add custom filter function --- filters.config.js | 36 ++++++++++++- src/@context/Filter.tsx | 3 +- src/@types/aquarius/BaseQueryParams.d.ts | 16 ++++++ src/@utils/aquarius/index.ts | 51 +++++++++++++++++- src/components/Search/Filter.tsx | 66 ++++++++++++++++++++---- src/components/Search/utils.ts | 42 +++++++++++++-- 6 files changed, 196 insertions(+), 18 deletions(-) diff --git a/filters.config.js b/filters.config.js index e1fd3aa4a..1569542ba 100644 --- a/filters.config.js +++ b/filters.config.js @@ -1,4 +1,38 @@ +const FILTER_VALUES = { + MUST_EXIST: 'MUST_EXIST', + MUST_EXISTS_AND_NON_EMPTY: 'MUST_EXISTS_AND_NON_EMPTY' + //... +} + module.exports = { - filters: [], + filters: [ + { + id: 'gaiax', + label: 'Gaia-X Service', + type: 'filterList', + options: [ + // a new filter value for a MUST_EXIST type could be added to handle this new functionality + { + label: 'Service SD', + value: FILTER_VALUES.MUST_EXISTS_AND_NON_EMPTY, + queryPath: + 'metadata.additionalInformation.gaiaXInformation.serviceSD.url' + }, + { + label: 'Terms and Conditions', + value: FILTER_VALUES.MUST_EXIST, + queryPath: + 'metadata.additionalInformation.gaiaXInformation.termsAndConditions' + }, + // options can have their own queryPath defined that gets validated against the defined value + { + label: 'Verified', + value: FILTER_VALUES.MUST_EXIST, + queryPath: + 'metadata.additionalInformation.gaiaXInformation.serviceSd.isValid' + } + ] + } + ], filterSets: {} } diff --git a/src/@context/Filter.tsx b/src/@context/Filter.tsx index 11b26de1a..ad08083bd 100644 --- a/src/@context/Filter.tsx +++ b/src/@context/Filter.tsx @@ -34,7 +34,8 @@ function FilterProvider({ children }: { children: ReactNode }): ReactElement { const [filters, setFilters] = useState({ accessType: [], serviceType: [], - filterSet: [] + filterSet: [], + gaiax: [] }) const [ignorePurgatory, setIgnorePurgatory] = useState(true) const [sort, setSort] = useState({ diff --git a/src/@types/aquarius/BaseQueryParams.d.ts b/src/@types/aquarius/BaseQueryParams.d.ts index eb5061c97..51d6cd24f 100644 --- a/src/@types/aquarius/BaseQueryParams.d.ts +++ b/src/@types/aquarius/BaseQueryParams.d.ts @@ -3,6 +3,21 @@ interface EsPaginationOptions { size?: number } +interface boolFilter { + bool: { + must: { + exists: { + field: string + } + } + must_not?: { + term: { + [key: string]: string + } + } + } +} + interface BaseQueryParams { chainIds: number[] // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -11,6 +26,7 @@ interface BaseQueryParams { sortOptions?: SortOptions aggs?: any filters?: FilterTerm[] + bool?: boolFilter[] ignorePurgatory?: boolean ignoreState?: boolean showSaas?: boolean diff --git a/src/@utils/aquarius/index.ts b/src/@utils/aquarius/index.ts index 6cc0858b7..01ad5a1f5 100644 --- a/src/@utils/aquarius/index.ts +++ b/src/@utils/aquarius/index.ts @@ -41,6 +41,18 @@ export function escapeEsReservedCharacters(value: string): string { */ type TFilterValue = string | number | boolean | number[] | string[] type TFilterKey = 'terms' | 'term' | 'match' | 'match_phrase' +type Query = { + must: { + exists: { + field: string + } + } + must_not?: { + term: { + [key: string]: string + } + } +} export function getFilterTerm( filterField: string, @@ -56,6 +68,39 @@ export function getFilterTerm( } } +export function getFilter(...args: any[]) { + let filters = [] + if (typeof args[0] === 'object') { + args[0].forEach((arg) => { + const filter = arg.split('=') + filters = [...filters, filter] + }) + } else { + const filter = args[0].split('=') + filters = [...filters, filter] + } + + let filter: Query[] = [] + filters.forEach((filterItem) => { + let query: Query = { + must: { + exists: { field: filterItem[0] } + } + } + if (filterItem[1] === 'MUST_EXISTS_AND_NON_EMPTY') { + query = { + ...query, + must_not: { + term: { [filterItem[0] + '.keyword']: '' } + } + } + } + filter = [...filter, query] + }) + + return filter +} + export function parseFilters( filtersList: Filters, filterSets: { [key: string]: string[] } @@ -77,6 +122,9 @@ export function parseFilters( ? getFilterTerm(filterQueryPath[key], uniqueTags) : undefined } + if (key === 'gaiax') { + return undefined + } if (filtersList[key].length > 0) return getFilterTerm(filterQueryPath[key], filtersList[key]) @@ -160,7 +208,8 @@ export function generateBaseQuery( ...(baseQueryParams.showSaas === false ? [saasFieldExists] : []) ] } - } + }, + ...(baseQueryParams.bool || []) ] } } diff --git a/src/components/Search/Filter.tsx b/src/components/Search/Filter.tsx index 98fca4dc0..cdc3cd020 100644 --- a/src/components/Search/Filter.tsx +++ b/src/components/Search/Filter.tsx @@ -23,6 +23,7 @@ interface FilterStructure { options: { label: string value: string + queryPath?: string }[] } @@ -114,10 +115,37 @@ export default function Filter({ router.push(urlLocation) } - async function handleSelectedFilter(value: string, filterId: string) { - const updatedFilters = filters[filterId].includes(value) - ? { ...filters, [filterId]: filters[filterId].filter((e) => e !== value) } - : { ...filters, [filterId]: [...filters[filterId], value] } + async function handleSelectedFilter( + value: { label: string; value: string; queryPath?: string }, + filterId: string + ) { + let updatedFilters + if (value.queryPath) { + updatedFilters = filters[filterId].includes( + `${value.queryPath}=${value.value}` + ) + ? { + ...filters, + [filterId]: filters[filterId].filter( + (e) => e !== `${value.queryPath}=${value.value}` + ) + } + : { + ...filters, + [filterId]: [ + ...filters[filterId], + `${value.queryPath}=${value.value}` + ] + } + } else { + updatedFilters = filters[filterId].includes(value.value) + ? { + ...filters, + [filterId]: filters[filterId].filter((e) => e !== value.value) + } + : { ...filters, [filterId]: [...filters[filterId], value.value] } + } + setFilters(updatedFilters) await applyFilter(updatedFilters[filterId], filterId) @@ -176,16 +204,25 @@ export default function Filter({
{filter.label}
{filter.options.map((option) => { - const isSelected = filters[filter.id].includes(option.value) + const isSelected = + filter.id === 'gaiax' + ? filters[filter.id].includes( + `${option.queryPath}=${option.value}` + ) + : filters[filter.id].includes(option.value) return ( { - handleSelectedFilter(option.value, filter.id) + handleSelectedFilter(option, filter.id) }} /> ) @@ -219,16 +256,25 @@ export default function Filter({ >
{filter.options.map((option) => { - const isSelected = filters[filter.id].includes(option.value) + const isSelected = + filter.id === 'gaiax' + ? filters[filter.id].includes( + `${option.queryPath}=${option.value}` + ) + : filters[filter.id].includes(option.value) return ( { - handleSelectedFilter(option.value, filter.id) + handleSelectedFilter(option, filter.id) }} /> ) diff --git a/src/components/Search/utils.ts b/src/components/Search/utils.ts index c20bc7a48..caa1f49a2 100644 --- a/src/components/Search/utils.ts +++ b/src/components/Search/utils.ts @@ -2,6 +2,7 @@ import { LoggerInstance } from '@oceanprotocol/lib' import { escapeEsReservedCharacters, generateBaseQuery, + getFilter, getFilterTerm, parseFilters, queryMetadata @@ -16,6 +17,21 @@ import { } from '../../@types/aquarius/SearchQuery' import { filterSets, getInitialFilters } from './Filter' +export interface boolFilter { + bool: { + must: { + exists: { + field: string + } + } + must_not?: { + term: { + [key: string]: string + } + } + } +} + export function updateQueryStringParameter( uri: string, key: string, @@ -43,15 +59,27 @@ export function getSearchQuery( serviceType?: string | string[], accessType?: string | string[], filterSet?: string | string[], - showSaas?: boolean + showSaas?: boolean, + gaiax?: string | string[] ): SearchQuery { text = escapeEsReservedCharacters(text) const emptySearchTerm = text === undefined || text === '' const filters: FilterTerm[] = [] + const bool: boolFilter[] = [] let searchTerm = text || '' let nestedQuery if (tags) { filters.push(getFilterTerm('metadata.tags.keyword', tags)) + } else if (gaiax) { + const filter = getFilter(gaiax) + filter.forEach((term) => { + const query = { + bool: { + ...term + } + } + bool.push(query) + }) } else { searchTerm = searchTerm.trim() const modifiedSearchTerm = searchTerm.split(' ').join(' OR ').trim() @@ -120,8 +148,8 @@ export function getSearchQuery( } const filtersList = getInitialFilters( - { accessType, serviceType, filterSet }, - ['accessType', 'serviceType', 'filterSet'] + { accessType, serviceType, filterSet, gaiax }, + ['accessType', 'serviceType', 'filterSet', 'gaiax'] ) parseFilters(filtersList, filterSets).forEach((term) => filters.push(term)) @@ -134,6 +162,7 @@ export function getSearchQuery( }, sortOptions: { sortBy: sort, sortDirection }, filters, + bool, showSaas } as BaseQueryParams @@ -154,6 +183,7 @@ export async function getResults( serviceType?: string | string[] accessType?: string | string[] filterSet?: string[] + gaiax?: string | string[] }, chainIds: number[], cancelToken?: CancelToken @@ -168,7 +198,8 @@ export async function getResults( sortOrder, serviceType, accessType, - filterSet + filterSet, + gaiax } = params const showSaas = @@ -199,7 +230,8 @@ export async function getResults( sanitizedServiceType, accessType, filterSet, - showSaas + showSaas, + gaiax ) const queryResult = await queryMetadata(searchQuery, cancelToken) From c2819f97b4d947a2a2aae17c7a548315f8562064 Mon Sep 17 00:00:00 2001 From: KevinRohlf <123676073+KevinRohlf@users.noreply.github.com> Date: Fri, 22 Nov 2024 19:51:24 +0100 Subject: [PATCH 2/6] chore: implements all filters to filters.config.js, move BoolFilterQuery interface and FILTER_VALUES, update getSearchQuery function --- filters.config.js | 31 ++++++++++++++---- src/@types/aquarius/BaseQueryParams.d.ts | 24 +++++++------- src/@types/aquarius/SearchQuery.ts | 5 +++ src/@utils/aquarius/index.ts | 25 ++++----------- src/components/Search/Filter.tsx | 35 +++++--------------- src/components/Search/utils.ts | 41 ++++++++---------------- 6 files changed, 70 insertions(+), 91 deletions(-) diff --git a/filters.config.js b/filters.config.js index 1569542ba..ee7564b96 100644 --- a/filters.config.js +++ b/filters.config.js @@ -1,17 +1,35 @@ -const FILTER_VALUES = { - MUST_EXIST: 'MUST_EXIST', - MUST_EXISTS_AND_NON_EMPTY: 'MUST_EXISTS_AND_NON_EMPTY' - //... -} +const { + FilterByTypeOptions, + FilterByAccessOptions, + FILTER_VALUES +} = require('src/@types/aquarius/SearchQuery') module.exports = { filters: [ + { + id: 'serviceType', + label: 'Service Type', + type: 'filterList', + options: [ + { label: 'datasets', value: FilterByTypeOptions.Data }, + { label: 'algorithms', value: FilterByTypeOptions.Algorithm }, + { label: 'saas', value: FilterByTypeOptions.Saas } + ] + }, + { + id: 'accessType', + label: 'Access Type', + type: 'filterList', + options: [ + { label: 'download', value: FilterByAccessOptions.Download }, + { label: 'compute', value: FilterByAccessOptions.Compute } + ] + }, { id: 'gaiax', label: 'Gaia-X Service', type: 'filterList', options: [ - // a new filter value for a MUST_EXIST type could be added to handle this new functionality { label: 'Service SD', value: FILTER_VALUES.MUST_EXISTS_AND_NON_EMPTY, @@ -24,7 +42,6 @@ module.exports = { queryPath: 'metadata.additionalInformation.gaiaXInformation.termsAndConditions' }, - // options can have their own queryPath defined that gets validated against the defined value { label: 'Verified', value: FILTER_VALUES.MUST_EXIST, diff --git a/src/@types/aquarius/BaseQueryParams.d.ts b/src/@types/aquarius/BaseQueryParams.d.ts index 51d6cd24f..b17fe2d6f 100644 --- a/src/@types/aquarius/BaseQueryParams.d.ts +++ b/src/@types/aquarius/BaseQueryParams.d.ts @@ -3,17 +3,19 @@ interface EsPaginationOptions { size?: number } -interface boolFilter { - bool: { - must: { - exists: { - field: string - } +interface BoolFilter { + bool: BoolFilterQuery +} + +interface BoolFilterQuery { + must: { + exists: { + field: string } - must_not?: { - term: { - [key: string]: string - } + } + must_not?: { + term: { + [key: string]: string } } } @@ -26,7 +28,7 @@ interface BaseQueryParams { sortOptions?: SortOptions aggs?: any filters?: FilterTerm[] - bool?: boolFilter[] + boolFilter?: BoolFilter[] ignorePurgatory?: boolean ignoreState?: boolean showSaas?: boolean diff --git a/src/@types/aquarius/SearchQuery.ts b/src/@types/aquarius/SearchQuery.ts index 5603d46c1..7c74c36a1 100644 --- a/src/@types/aquarius/SearchQuery.ts +++ b/src/@types/aquarius/SearchQuery.ts @@ -32,6 +32,11 @@ export enum FilterByAccessOptions { Compute = 'compute' } +export enum FILTER_VALUES { + MUST_EXIST = 'MUST_EXIST', // checks if the queryPath exists + MUST_EXISTS_AND_NON_EMPTY = 'MUST_EXISTS_AND_NON_EMPTY' // checks if the queryPath exists and is not empty +} + declare global { interface SortOptions { sortBy: SortTermOptions diff --git a/src/@utils/aquarius/index.ts b/src/@utils/aquarius/index.ts index 01ad5a1f5..a9289c240 100644 --- a/src/@utils/aquarius/index.ts +++ b/src/@utils/aquarius/index.ts @@ -4,6 +4,7 @@ import axios, { CancelToken, AxiosResponse } from 'axios' import { OrdersData_orders as OrdersData } from '../../@types/subgraph/OrdersData' import { metadataCacheUri, allowDynamicPricing } from '../../../app.config' import { + FILTER_VALUES, FilterByTypeOptions, SortDirectionOptions, SortTermOptions @@ -41,18 +42,6 @@ export function escapeEsReservedCharacters(value: string): string { */ type TFilterValue = string | number | boolean | number[] | string[] type TFilterKey = 'terms' | 'term' | 'match' | 'match_phrase' -type Query = { - must: { - exists: { - field: string - } - } - must_not?: { - term: { - [key: string]: string - } - } -} export function getFilterTerm( filterField: string, @@ -71,23 +60,23 @@ export function getFilterTerm( export function getFilter(...args: any[]) { let filters = [] if (typeof args[0] === 'object') { - args[0].forEach((arg) => { + for (const arg of args[0]) { const filter = arg.split('=') filters = [...filters, filter] - }) + } } else { const filter = args[0].split('=') filters = [...filters, filter] } - let filter: Query[] = [] + let filter: BoolFilterQuery[] = [] filters.forEach((filterItem) => { - let query: Query = { + let query: BoolFilterQuery = { must: { exists: { field: filterItem[0] } } } - if (filterItem[1] === 'MUST_EXISTS_AND_NON_EMPTY') { + if (filterItem[1] === FILTER_VALUES.MUST_EXISTS_AND_NON_EMPTY) { query = { ...query, must_not: { @@ -209,7 +198,7 @@ export function generateBaseQuery( ] } }, - ...(baseQueryParams.bool || []) + ...(baseQueryParams.boolFilter || []) ] } } diff --git a/src/components/Search/Filter.tsx b/src/components/Search/Filter.tsx index cdc3cd020..816137683 100644 --- a/src/components/Search/Filter.tsx +++ b/src/components/Search/Filter.tsx @@ -28,25 +28,6 @@ interface FilterStructure { } const filterList: FilterStructure[] = [ - { - id: 'serviceType', - label: 'Service Type', - type: 'filterList', - options: [ - { label: 'datasets', value: FilterByTypeOptions.Data }, - { label: 'algorithms', value: FilterByTypeOptions.Algorithm }, - { label: 'saas', value: FilterByTypeOptions.Saas } - ] - }, - { - id: 'accessType', - label: 'Access Type', - type: 'filterList', - options: [ - { label: 'download', value: FilterByAccessOptions.Download }, - { label: 'compute', value: FilterByAccessOptions.Compute } - ] - }, ...(Array.isArray(customFilters?.filters) && customFilters?.filters?.length > 0 && customFilters?.filters.some((filter) => filter !== undefined) @@ -116,34 +97,34 @@ export default function Filter({ } async function handleSelectedFilter( - value: { label: string; value: string; queryPath?: string }, + option: { label: string; value: string; queryPath?: string }, filterId: string ) { let updatedFilters - if (value.queryPath) { + if (option.queryPath) { updatedFilters = filters[filterId].includes( - `${value.queryPath}=${value.value}` + `${option.queryPath}=${option.value}` ) ? { ...filters, [filterId]: filters[filterId].filter( - (e) => e !== `${value.queryPath}=${value.value}` + (e) => e !== `${option.queryPath}=${option.value}` ) } : { ...filters, [filterId]: [ ...filters[filterId], - `${value.queryPath}=${value.value}` + `${option.queryPath}=${option.value}` ] } } else { - updatedFilters = filters[filterId].includes(value.value) + updatedFilters = filters[filterId].includes(option.value) ? { ...filters, - [filterId]: filters[filterId].filter((e) => e !== value.value) + [filterId]: filters[filterId].filter((e) => e !== option.value) } - : { ...filters, [filterId]: [...filters[filterId], value.value] } + : { ...filters, [filterId]: [...filters[filterId], option.value] } } setFilters(updatedFilters) diff --git a/src/components/Search/utils.ts b/src/components/Search/utils.ts index caa1f49a2..97e66eb8b 100644 --- a/src/components/Search/utils.ts +++ b/src/components/Search/utils.ts @@ -17,21 +17,6 @@ import { } from '../../@types/aquarius/SearchQuery' import { filterSets, getInitialFilters } from './Filter' -export interface boolFilter { - bool: { - must: { - exists: { - field: string - } - } - must_not?: { - term: { - [key: string]: string - } - } - } -} - export function updateQueryStringParameter( uri: string, key: string, @@ -65,22 +50,12 @@ export function getSearchQuery( text = escapeEsReservedCharacters(text) const emptySearchTerm = text === undefined || text === '' const filters: FilterTerm[] = [] - const bool: boolFilter[] = [] + const boolFilter: BoolFilter[] = [] let searchTerm = text || '' let nestedQuery if (tags) { filters.push(getFilterTerm('metadata.tags.keyword', tags)) - } else if (gaiax) { - const filter = getFilter(gaiax) - filter.forEach((term) => { - const query = { - bool: { - ...term - } - } - bool.push(query) - }) - } else { + } else if (!gaiax) { searchTerm = searchTerm.trim() const modifiedSearchTerm = searchTerm.split(' ').join(' OR ').trim() const noSpaceSearchTerm = searchTerm.split(' ').join('').trim() @@ -145,6 +120,16 @@ export function getSearchQuery( } ] } + } else { + const filter = getFilter(gaiax) + filter.forEach((term) => { + const query = { + bool: { + ...term + } + } + boolFilter.push(query) + }) } const filtersList = getInitialFilters( @@ -162,7 +147,7 @@ export function getSearchQuery( }, sortOptions: { sortBy: sort, sortDirection }, filters, - bool, + boolFilter, showSaas } as BaseQueryParams From 401b6907d1115a79e841f28a79c669c60520f81c Mon Sep 17 00:00:00 2001 From: KevinRohlf <123676073+KevinRohlf@users.noreply.github.com> Date: Tue, 3 Dec 2024 08:04:33 +0100 Subject: [PATCH 3/6] feat: enhance filter functionality with dynamic query paths and improved type handling --- filters.config.js | 2 + src/@types/aquarius/BaseQueryParams.d.ts | 17 +++-- src/@utils/aquarius/index.ts | 64 ++++++++++------- src/components/Search/Filter.tsx | 88 ++++++++++-------------- src/components/Search/utils.ts | 21 +++--- 5 files changed, 101 insertions(+), 91 deletions(-) diff --git a/filters.config.js b/filters.config.js index ee7564b96..51ec32947 100644 --- a/filters.config.js +++ b/filters.config.js @@ -10,6 +10,7 @@ module.exports = { id: 'serviceType', label: 'Service Type', type: 'filterList', + queryPath: 'metadata.type', options: [ { label: 'datasets', value: FilterByTypeOptions.Data }, { label: 'algorithms', value: FilterByTypeOptions.Algorithm }, @@ -20,6 +21,7 @@ module.exports = { id: 'accessType', label: 'Access Type', type: 'filterList', + queryPath: 'services.type', options: [ { label: 'download', value: FilterByAccessOptions.Download }, { label: 'compute', value: FilterByAccessOptions.Compute } diff --git a/src/@types/aquarius/BaseQueryParams.d.ts b/src/@types/aquarius/BaseQueryParams.d.ts index b17fe2d6f..96364eef5 100644 --- a/src/@types/aquarius/BaseQueryParams.d.ts +++ b/src/@types/aquarius/BaseQueryParams.d.ts @@ -3,19 +3,24 @@ interface EsPaginationOptions { size?: number } -interface BoolFilter { - bool: BoolFilterQuery +type FieldPath = string + +interface BoolFilter { + bool: MustExistQuery & MustNotTermQuery } -interface BoolFilterQuery { +interface MustExistQuery { must: { exists: { - field: string + field: T } } +} + +interface MustNotTermQuery { must_not?: { term: { - [key: string]: string + [field in T]: string } } } @@ -28,7 +33,7 @@ interface BaseQueryParams { sortOptions?: SortOptions aggs?: any filters?: FilterTerm[] - boolFilter?: BoolFilter[] + boolFilter?: BoolFilter ignorePurgatory?: boolean ignoreState?: boolean showSaas?: boolean diff --git a/src/@utils/aquarius/index.ts b/src/@utils/aquarius/index.ts index a9289c240..100872edf 100644 --- a/src/@utils/aquarius/index.ts +++ b/src/@utils/aquarius/index.ts @@ -50,28 +50,49 @@ export function getFilterTerm( ): FilterTerm { const isArray = Array.isArray(value) const useKey = key === 'term' ? (isArray ? 'terms' : 'term') : key - return { - [useKey]: { - [filterField]: value + const filters = [] + let field + if (isArray) { + for (const val of value) { + if (typeof val === 'string' && val.includes('=')) { + const [fieldFilter, values] = val.split('=') as [string, string] + field = fieldFilter + filters.push(values) + } + } + } + if (filters.length > 0) { + return { + [useKey]: { + [field]: filters + } + } + } else if (filterField) { + return { + [useKey]: { + [filterField]: value + } } } } -export function getFilter(...args: any[]) { +function splitArg(arg: string) { + return arg.split('=') +} + +export function getFilter(args: string | string[]) { let filters = [] - if (typeof args[0] === 'object') { - for (const arg of args[0]) { - const filter = arg.split('=') - filters = [...filters, filter] + if (typeof args === 'object') { + for (const arg of args) { + filters = [...filters, splitArg(arg)] } } else { - const filter = args[0].split('=') - filters = [...filters, filter] + filters = [...filters, splitArg(args)] } - let filter: BoolFilterQuery[] = [] + let filter: (MustNotTermQuery & MustExistQuery)[] = [] filters.forEach((filterItem) => { - let query: BoolFilterQuery = { + let query: MustNotTermQuery & MustExistQuery = { must: { exists: { field: filterItem[0] } } @@ -94,12 +115,6 @@ export function parseFilters( filtersList: Filters, filterSets: { [key: string]: string[] } ): FilterTerm[] { - const filterQueryPath = { - accessType: 'services.type', - serviceType: 'metadata.type', - filterSet: 'metadata.tags.keyword' - } - const filterTerms = Object.keys(filtersList)?.map((key) => { if (key === 'filterSet') { const tags = filtersList[key].reduce( @@ -107,15 +122,13 @@ export function parseFilters( [] ) const uniqueTags = [...new Set(tags)] - return uniqueTags.length > 0 - ? getFilterTerm(filterQueryPath[key], uniqueTags) - : undefined + return uniqueTags.length > 0 ? getFilterTerm(null, uniqueTags) : undefined } if (key === 'gaiax') { return undefined } if (filtersList[key].length > 0) - return getFilterTerm(filterQueryPath[key], filtersList[key]) + return getFilterTerm(null, filtersList[key]) return undefined }) @@ -197,8 +210,7 @@ export function generateBaseQuery( ...(baseQueryParams.showSaas === false ? [saasFieldExists] : []) ] } - }, - ...(baseQueryParams.boolFilter || []) + } ] } } @@ -208,6 +220,10 @@ export function generateBaseQuery( generatedQuery.aggs = baseQueryParams.aggs } + if (baseQueryParams.boolFilter !== undefined) { + generatedQuery.query.bool.filter.push(...baseQueryParams.boolFilter) + } + if (baseQueryParams.sortOptions !== undefined) generatedQuery.sort = { [baseQueryParams.sortOptions.sortBy]: diff --git a/src/components/Search/Filter.tsx b/src/components/Search/Filter.tsx index 816137683..2aacf305d 100644 --- a/src/components/Search/Filter.tsx +++ b/src/components/Search/Filter.tsx @@ -20,6 +20,7 @@ interface FilterStructure { id: string label: string type: string + queryPath?: string options: { label: string value: string @@ -98,35 +99,23 @@ export default function Filter({ async function handleSelectedFilter( option: { label: string; value: string; queryPath?: string }, - filterId: string + filterId: string, + queryPath?: string ) { - let updatedFilters - if (option.queryPath) { - updatedFilters = filters[filterId].includes( - `${option.queryPath}=${option.value}` - ) - ? { - ...filters, - [filterId]: filters[filterId].filter( - (e) => e !== `${option.queryPath}=${option.value}` - ) - } - : { - ...filters, - [filterId]: [ - ...filters[filterId], - `${option.queryPath}=${option.value}` - ] - } - } else { - updatedFilters = filters[filterId].includes(option.value) - ? { - ...filters, - [filterId]: filters[filterId].filter((e) => e !== option.value) - } - : { ...filters, [filterId]: [...filters[filterId], option.value] } - } - + const getFilterQueryString = `${queryPath || option.queryPath}=${ + option.value + }` + const updatedFilters = filters[filterId].includes(getFilterQueryString) + ? { + ...filters, + [filterId]: filters[filterId].filter( + (filter) => filter !== getFilterQueryString + ) + } + : { + ...filters, + [filterId]: [...filters[filterId], getFilterQueryString] + } setFilters(updatedFilters) await applyFilter(updatedFilters[filterId], filterId) @@ -185,25 +174,22 @@ export default function Filter({
{filter.label}
{filter.options.map((option) => { - const isSelected = - filter.id === 'gaiax' - ? filters[filter.id].includes( - `${option.queryPath}=${option.value}` - ) - : filters[filter.id].includes(option.value) + const isSelected = filters[filter.id].includes( + `${filter.queryPath || option.queryPath}=${option.value}` + ) return ( { - handleSelectedFilter(option, filter.id) + handleSelectedFilter( + option, + filter.id, + filter?.queryPath + ) }} /> ) @@ -237,25 +223,23 @@ export default function Filter({ >
{filter.options.map((option) => { - const isSelected = - filter.id === 'gaiax' - ? filters[filter.id].includes( - `${option.queryPath}=${option.value}` - ) - : filters[filter.id].includes(option.value) + const isSelected = filters[filter.id].includes( + `${option.queryPath}=${option.value}` + ) + return ( { - handleSelectedFilter(option, filter.id) + handleSelectedFilter( + option, + filter.id, + filter?.queryPath + ) }} /> ) diff --git a/src/components/Search/utils.ts b/src/components/Search/utils.ts index 97e66eb8b..d5f002401 100644 --- a/src/components/Search/utils.ts +++ b/src/components/Search/utils.ts @@ -45,12 +45,13 @@ export function getSearchQuery( accessType?: string | string[], filterSet?: string | string[], showSaas?: boolean, - gaiax?: string | string[] + gaiax?: string | string[], + custom?: string | string[] ): SearchQuery { text = escapeEsReservedCharacters(text) const emptySearchTerm = text === undefined || text === '' const filters: FilterTerm[] = [] - const boolFilter: BoolFilter[] = [] + const boolFilter: BoolFilter[] = [] let searchTerm = text || '' let nestedQuery if (tags) { @@ -133,8 +134,8 @@ export function getSearchQuery( } const filtersList = getInitialFilters( - { accessType, serviceType, filterSet, gaiax }, - ['accessType', 'serviceType', 'filterSet', 'gaiax'] + { accessType, serviceType, filterSet, gaiax, custom }, + ['accessType', 'serviceType', 'filterSet', 'gaiax', 'custom'] ) parseFilters(filtersList, filterSets).forEach((term) => filters.push(term)) @@ -169,6 +170,7 @@ export async function getResults( accessType?: string | string[] filterSet?: string[] gaiax?: string | string[] + custom?: string | string[] }, chainIds: number[], cancelToken?: CancelToken @@ -190,16 +192,17 @@ export async function getResults( const showSaas = serviceType === undefined ? undefined - : serviceType === FilterByTypeOptions.Saas || + : serviceType === 'metadata.type=' + FilterByTypeOptions.Saas || (typeof serviceType !== 'string' && - serviceType.includes(FilterByTypeOptions.Saas)) - + serviceType.includes('metadata.type=' + FilterByTypeOptions.Saas)) // we make sure to query only for service types that are expected // by Aqua ("dataset" or "algorithm") by removing "saas" const sanitizedServiceType = serviceType !== undefined && typeof serviceType !== 'string' - ? serviceType.filter((type) => type !== FilterByTypeOptions.Saas) - : serviceType === FilterByTypeOptions.Saas + ? serviceType.filter( + (type) => type !== 'metadata.type=' + FilterByTypeOptions.Saas + ) + : serviceType === 'metadata.type=' + FilterByTypeOptions.Saas ? undefined : serviceType From 303f283817df0784b019f41bc392142283de82e9 Mon Sep 17 00:00:00 2001 From: KevinRohlf <123676073+KevinRohlf@users.noreply.github.com> Date: Thu, 5 Dec 2024 07:12:01 +0100 Subject: [PATCH 4/6] fix: update filter component keys --- src/components/Search/Filter.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/Search/Filter.tsx b/src/components/Search/Filter.tsx index 2aacf305d..d360ac558 100644 --- a/src/components/Search/Filter.tsx +++ b/src/components/Search/Filter.tsx @@ -170,8 +170,8 @@ export default function Filter({ } >
- {filterList.map((filter) => ( -
+ {filterList.map((filter, index) => ( +
{filter.label}
{filter.options.map((option) => { const isSelected = filters[filter.id].includes( @@ -214,8 +214,11 @@ export default function Filter({
- {filterList.map((filter) => ( -
+ {filterList.map((filter, index) => ( +
Date: Thu, 5 Dec 2024 08:22:54 +0100 Subject: [PATCH 5/6] fix: isSelected on top position --- src/@context/Filter.tsx | 3 ++- src/components/Search/Filter.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/@context/Filter.tsx b/src/@context/Filter.tsx index ad08083bd..eb5ec6298 100644 --- a/src/@context/Filter.tsx +++ b/src/@context/Filter.tsx @@ -35,7 +35,8 @@ function FilterProvider({ children }: { children: ReactNode }): ReactElement { accessType: [], serviceType: [], filterSet: [], - gaiax: [] + gaiax: [], + custom: [] }) const [ignorePurgatory, setIgnorePurgatory] = useState(true) const [sort, setSort] = useState({ diff --git a/src/components/Search/Filter.tsx b/src/components/Search/Filter.tsx index d360ac558..44df72d03 100644 --- a/src/components/Search/Filter.tsx +++ b/src/components/Search/Filter.tsx @@ -227,7 +227,7 @@ export default function Filter({
{filter.options.map((option) => { const isSelected = filters[filter.id].includes( - `${option.queryPath}=${option.value}` + `${filter.queryPath || option.queryPath}=${option.value}` ) return ( From 581edd0309285cf02b8ec20521c40ea200466158 Mon Sep 17 00:00:00 2001 From: KevinRohlf <123676073+KevinRohlf@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:57:37 +0100 Subject: [PATCH 6/6] feat: add MUST_EXIST & MUSTS_EXITS_AND_NON_EMPTY filters for all ids --- src/@utils/aquarius/index.ts | 8 ++-- src/components/Search/Filter.tsx | 6 +-- src/components/Search/utils.ts | 72 +++++++++++++++++++++++++++----- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/src/@utils/aquarius/index.ts b/src/@utils/aquarius/index.ts index 100872edf..2069943a7 100644 --- a/src/@utils/aquarius/index.ts +++ b/src/@utils/aquarius/index.ts @@ -116,6 +116,11 @@ export function parseFilters( filterSets: { [key: string]: string[] } ): FilterTerm[] { const filterTerms = Object.keys(filtersList)?.map((key) => { + filtersList[key] = filtersList[key].filter( + (filter) => + !filter.includes(FILTER_VALUES.MUST_EXISTS_AND_NON_EMPTY) && + !filter.includes(FILTER_VALUES.MUST_EXIST) + ) if (key === 'filterSet') { const tags = filtersList[key].reduce( (acc, set) => [...acc, ...filterSets[set]], @@ -124,9 +129,6 @@ export function parseFilters( const uniqueTags = [...new Set(tags)] return uniqueTags.length > 0 ? getFilterTerm(null, uniqueTags) : undefined } - if (key === 'gaiax') { - return undefined - } if (filtersList[key].length > 0) return getFilterTerm(null, filtersList[key]) diff --git a/src/components/Search/Filter.tsx b/src/components/Search/Filter.tsx index 44df72d03..8d5ee6575 100644 --- a/src/components/Search/Filter.tsx +++ b/src/components/Search/Filter.tsx @@ -102,7 +102,7 @@ export default function Filter({ filterId: string, queryPath?: string ) { - const getFilterQueryString = `${queryPath || option.queryPath}=${ + const getFilterQueryString = `${option.queryPath || queryPath}=${ option.value }` const updatedFilters = filters[filterId].includes(getFilterQueryString) @@ -175,7 +175,7 @@ export default function Filter({
{filter.label}
{filter.options.map((option) => { const isSelected = filters[filter.id].includes( - `${filter.queryPath || option.queryPath}=${option.value}` + `${option.queryPath || filter.queryPath}=${option.value}` ) return ( {filter.options.map((option) => { const isSelected = filters[filter.id].includes( - `${filter.queryPath || option.queryPath}=${option.value}` + `${option.queryPath || filter.queryPath}=${option.value}` ) return ( diff --git a/src/components/Search/utils.ts b/src/components/Search/utils.ts index d5f002401..3325901b1 100644 --- a/src/components/Search/utils.ts +++ b/src/components/Search/utils.ts @@ -10,6 +10,7 @@ import { import queryString from 'query-string' import { CancelToken } from 'axios' import { + FILTER_VALUES, FilterByAccessOptions, FilterByTypeOptions, SortDirectionOptions, @@ -52,11 +53,34 @@ export function getSearchQuery( const emptySearchTerm = text === undefined || text === '' const filters: FilterTerm[] = [] const boolFilter: BoolFilter[] = [] - let searchTerm = text || '' + const filterList = [filterSet, gaiax, custom, accessType, serviceType] + const checkMustExists = () => { + return filterList.some((filter) => { + let isValueMustExist = + filter?.includes(FILTER_VALUES.MUST_EXIST) || + filter?.includes(FILTER_VALUES.MUST_EXISTS_AND_NON_EMPTY) + const isArray = typeof filter !== 'string' + if (isArray) { + isValueMustExist = filter?.some((term) => { + console.log(term) + return ( + term.includes(FILTER_VALUES.MUST_EXIST) || + term.includes(FILTER_VALUES.MUST_EXISTS_AND_NON_EMPTY) + ) + }) + } + + if (isValueMustExist) { + console.log(filter, 'true') + return true + } + console.log(filter, 'false') + return false + }) + } + const searchTerm = text || '' let nestedQuery - if (tags) { - filters.push(getFilterTerm('metadata.tags.keyword', tags)) - } else if (!gaiax) { + const getSearchTerm = (searchTerm: string) => { searchTerm = searchTerm.trim() const modifiedSearchTerm = searchTerm.split(' ').join(' OR ').trim() const noSpaceSearchTerm = searchTerm.split(' ').join('').trim() @@ -121,16 +145,42 @@ export function getSearchQuery( } ] } + } + const getMustExistFilter = (filter: string) => { + if ( + filter.includes(FILTER_VALUES.MUST_EXIST) || + filter.includes(FILTER_VALUES.MUST_EXISTS_AND_NON_EMPTY) + ) { + const filters = getFilter(filter) + filters.forEach((term) => { + const query = { + bool: { + ...term + } + } + boolFilter.push(query) + }) + } else { + getSearchTerm(searchTerm) + } + } + if (tags) { + filters.push(getFilterTerm('metadata.tags.keyword', tags)) + } else if (!checkMustExists()) { + getSearchTerm(searchTerm) } else { - const filter = getFilter(gaiax) - filter.forEach((term) => { - const query = { - bool: { - ...term + for (const filters of filterList) { + if (typeof filters !== 'undefined') { + const isArray = typeof filters !== 'string' + if (isArray) { + for (const filter of filters) { + getMustExistFilter(filter) + } + } else { + getMustExistFilter(filters) } } - boolFilter.push(query) - }) + } } const filtersList = getInitialFilters(