Browse Source

Merge pull request #8479 from nocodb/nc-fix/misc-oss

Miscellaneous bug fixes
pull/8484/head
Pranav C 2 months ago committed by GitHub
parent
commit
da186b74ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 39
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
  2. 44
      packages/nc-gui/composables/useViewFilters.ts
  3. 3
      packages/nc-gui/store/bases.ts
  4. 438
      packages/nocodb/src/helpers/webhookHelpers.ts
  5. 4
      packages/nocodb/src/models/Filter.ts

39
packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue

@ -12,6 +12,7 @@ interface Props {
webHook?: boolean webHook?: boolean
draftFilter?: Partial<FilterType> draftFilter?: Partial<FilterType>
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
nestedLevel: 0, nestedLevel: 0,
autoSave: true, autoSave: true,
@ -21,13 +22,14 @@ const props = withDefaults(defineProps<Props>(), {
webHook: false, webHook: false,
}) })
const emit = defineEmits(['update:filtersLength', 'update:draftFilter']) const emit = defineEmits(['update:filtersLength', 'update:draftFilter', 'update:modelValue'])
const excludedFilterColUidt = [UITypes.QrCode, UITypes.Barcode] const excludedFilterColUidt = [UITypes.QrCode, UITypes.Barcode]
const draftFilter = useVModel(props, 'draftFilter', emit) const draftFilter = useVModel(props, 'draftFilter', emit)
const modelValue = useVModel(props, 'modelValue', emit)
const { nestedLevel, parentId, autoSave, hookId, modelValue, showLoading, webHook } = toRefs(props) const { nestedLevel, parentId, autoSave, hookId, showLoading, webHook } = toRefs(props)
const nested = computed(() => nestedLevel.value > 0) const nested = computed(() => nestedLevel.value > 0)
@ -66,7 +68,7 @@ const {
types, types,
} = useViewFilters( } = useViewFilters(
activeView, activeView,
parentId?.value, parentId,
computed(() => autoSave.value), computed(() => autoSave.value),
() => reloadDataHook.trigger({ shouldShowLoading: showLoading.value, offset: 0 }), () => reloadDataHook.trigger({ shouldShowLoading: showLoading.value, offset: 0 }),
modelValue.value || nestedFilters.value, modelValue.value || nestedFilters.value,
@ -250,7 +252,7 @@ const updateFilterValue = (value: string, filter: Filter, index: number) => {
defineExpose({ defineExpose({
applyChanges, applyChanges,
parentId: parentId?.value, parentId,
}) })
const scrollToBottom = () => { const scrollToBottom = () => {
@ -355,22 +357,35 @@ watch(
}, },
) )
const visibleFilters = computed(() => filters.value.filter((filter) => filter.status !== 'delete'))
const isLogicalOpChangeAllowed = computed(() => { const isLogicalOpChangeAllowed = computed(() => {
return new Set(filters.value.slice(1).map((filter) => filter.logical_op)).size > 1 return new Set(visibleFilters.value.slice(1).map((filter) => filter.logical_op)).size > 1
}) })
// when logical operation is updated, update all the siblings with the same logical operation only if it's in locked state // when logical operation is updated, update all the siblings with the same logical operation only if it's in locked state
const onLogicalOpUpdate = async (filter: Filter, index: number) => { const onLogicalOpUpdate = async (filter: Filter, index: number) => {
if (index === 1 && filters.value.slice(2).every((siblingFilter) => siblingFilter.logical_op !== filter.logical_op)) { if (index === 1 && visibleFilters.value.slice(2).every((siblingFilter) => siblingFilter.logical_op !== filter.logical_op)) {
await Promise.all( await Promise.all(
filters.value.slice(2).map(async (siblingFilter, i) => { visibleFilters.value.slice(2).map(async (siblingFilter, i) => {
siblingFilter.logical_op = filter.logical_op siblingFilter.logical_op = filter.logical_op
await saveOrUpdate(siblingFilter, i + 2, false, false, true) await saveOrUpdate(siblingFilter, i + 2, false, false, true)
}), }),
) )
} }
await filterUpdateCondition(filter, index) await saveOrUpdate(filter, index)
} }
// watch for changes in filters and update the modelValue
watch(
filters,
() => {
if (modelValue.value !== filters.value) modelValue.value = filters.value
},
{
immediate: true,
},
)
</script> </script>
<template> <template>
@ -403,9 +418,9 @@ const onLogicalOpUpdate = async (filter: Filter, index: number) => {
class="min-w-20 capitalize" class="min-w-20 capitalize"
placeholder="Group op" placeholder="Group op"
dropdown-class-name="nc-dropdown-filter-logical-op-group" dropdown-class-name="nc-dropdown-filter-logical-op-group"
:disabled="i > 1 && !isLogicalOpChangeAllowed" :disabled="visibleFilters.indexOf(filter) > 1 && !isLogicalOpChangeAllowed"
@click.stop @click.stop
@change="saveOrUpdate(filter, i)" @change="onLogicalOpUpdate(filter, i)"
> >
<a-select-option v-for="op in logicalOps" :key="op.value" :value="op.value"> <a-select-option v-for="op in logicalOps" :key="op.value" :value="op.value">
<div class="flex items-center w-full justify-between w-full gap-2"> <div class="flex items-center w-full justify-between w-full gap-2">
@ -434,7 +449,7 @@ const onLogicalOpUpdate = async (filter: Filter, index: number) => {
</div> </div>
<div class="flex border-1 rounded-lg p-2 w-full" :class="nestedLevel % 2 !== 0 ? 'bg-white' : 'bg-gray-100'"> <div class="flex border-1 rounded-lg p-2 w-full" :class="nestedLevel % 2 !== 0 ? 'bg-white' : 'bg-gray-100'">
<LazySmartsheetToolbarColumnFilter <LazySmartsheetToolbarColumnFilter
v-if="filter.id || filter.children" v-if="filter.id || filter.children || !autoSave"
:key="filter.id ?? i" :key="filter.id ?? i"
ref="localNestedFilters" ref="localNestedFilters"
v-model="filter.children" v-model="filter.children"
@ -456,7 +471,7 @@ const onLogicalOpUpdate = async (filter: Filter, index: number) => {
:dropdown-match-select-width="false" :dropdown-match-select-width="false"
class="h-full !min-w-20 !max-w-20 capitalize" class="h-full !min-w-20 !max-w-20 capitalize"
hide-details hide-details
:disabled="filter.readOnly || (i > 1 && !isLogicalOpChangeAllowed)" :disabled="filter.readOnly || (visibleFilters.indexOf(filter) > 1 && !isLogicalOpChangeAllowed)"
dropdown-class-name="nc-dropdown-filter-logical-op" dropdown-class-name="nc-dropdown-filter-logical-op"
@change="onLogicalOpUpdate(filter, i)" @change="onLogicalOpUpdate(filter, i)"
@click.stop @click.stop

44
packages/nc-gui/composables/useViewFilters.ts

@ -12,13 +12,15 @@ import { UITypes, isSystemColumn } from 'nocodb-sdk'
export function useViewFilters( export function useViewFilters(
view: Ref<ViewType | undefined>, view: Ref<ViewType | undefined>,
parentId?: string, _parentId: Ref<string | null> | null | string,
autoApply?: ComputedRef<boolean>, autoApply?: ComputedRef<boolean>,
reloadData?: () => void, reloadData?: () => void,
_currentFilters?: Filter[], _currentFilters?: Filter[],
isNestedRoot?: boolean, isNestedRoot?: boolean,
isWebhook?: boolean, isWebhook?: boolean,
) { ) {
const parentId = ref(_parentId)
const currentFilters = ref(_currentFilters) const currentFilters = ref(_currentFilters)
const btLookupTypesMap = ref({}) const btLookupTypesMap = ref({})
@ -192,6 +194,16 @@ export function useViewFilters(
} }
} }
const placeholderGroupFilter = (): Filter => {
const logicalOps = new Set(filters.value.slice(1).map((filter) => filter.logical_op))
return {
is_group: true,
status: 'create',
logical_op: logicalOps.size === 1 ? logicalOps.values().next().value : 'and',
}
}
const loadAllChildFilters = async (filters: Filter[]) => { const loadAllChildFilters = async (filters: Filter[]) => {
// Array to store promises of child filter loading // Array to store promises of child filter loading
const promises = [] const promises = []
@ -230,14 +242,14 @@ export function useViewFilters(
try { try {
if (isWebhook || hookId) { if (isWebhook || hookId) {
if (parentId) { if (parentId.value) {
filters.value = (await $api.dbTableFilter.childrenRead(parentId)).list as Filter[] filters.value = (await $api.dbTableFilter.childrenRead(parentId.value)).list as Filter[]
} else if (hookId) { } else if (hookId) {
filters.value = (await $api.dbTableWebhookFilter.read(hookId)).list as Filter[] filters.value = (await $api.dbTableWebhookFilter.read(hookId)).list as Filter[]
} }
} else { } else {
if (parentId) { if (parentId.value) {
filters.value = (await $api.dbTableFilter.childrenRead(parentId)).list as Filter[] filters.value = (await $api.dbTableFilter.childrenRead(parentId.value)).list as Filter[]
} else { } else {
filters.value = (await $api.dbTableFilter.read(view.value!.id!)).list as Filter[] filters.value = (await $api.dbTableFilter.read(view.value!.id!)).list as Filter[]
if (loadAllFilters) { if (loadAllFilters) {
@ -265,21 +277,25 @@ export function useViewFilters(
} else if (filter.status === 'update') { } else if (filter.status === 'update') {
await $api.dbTableFilter.update(filter.id as string, { await $api.dbTableFilter.update(filter.id as string, {
...filter, ...filter,
fk_parent_id: parentId, fk_parent_id: parentId.value,
}) })
} else if (filter.status === 'create') { } else if (filter.status === 'create') {
// extract children value if found to restore
const children = filters.value[+i]?.children
if (hookId) { if (hookId) {
filters.value[+i] = (await $api.dbTableWebhookFilter.create(hookId, { filters.value[+i] = (await $api.dbTableWebhookFilter.create(hookId, {
...filter, ...filter,
fk_parent_id: parentId, fk_parent_id: parentId.value,
})) as unknown as FilterType })) as unknown as FilterType
} else { } else {
filters.value[+i] = await $api.dbTableFilter.create(view?.value?.id as string, { filters.value[+i] = await $api.dbTableFilter.create(view?.value?.id as string, {
...filter, ...filter,
fk_parent_id: parentId, fk_parent_id: parentId.value,
}) })
} }
if (children) filters.value[+i].children = children
allFilters.value.push(filters.value[+i]) allFilters.value.push(filters.value[+i])
} }
} }
@ -335,7 +351,7 @@ export function useViewFilters(
} else if (filter.id && filter.status !== 'create') { } else if (filter.id && filter.status !== 'create') {
await $api.dbTableFilter.update(filter.id, { await $api.dbTableFilter.update(filter.id, {
...filter, ...filter,
fk_parent_id: parentId, fk_parent_id: parentId.value,
}) })
$e('a:filter:update', { $e('a:filter:update', {
logical: filter.logical_op, logical: filter.logical_op,
@ -344,7 +360,7 @@ export function useViewFilters(
} else { } else {
filters.value[i] = await $api.dbTableFilter.create(view.value.id!, { filters.value[i] = await $api.dbTableFilter.create(view.value.id!, {
...filter, ...filter,
fk_parent_id: parentId, fk_parent_id: parentId.value,
}) })
allFilters.value.push(filters.value[+i]) allFilters.value.push(filters.value[+i])
@ -464,11 +480,7 @@ export function useViewFilters(
const addFilterGroup = async () => { const addFilterGroup = async () => {
const child = placeholderFilter() const child = placeholderFilter()
const placeHolderGroupFilter: Filter = { const placeHolderGroupFilter: Filter = placeholderGroupFilter()
is_group: true,
status: 'create',
logical_op: 'and',
}
if (nestedMode.value) placeHolderGroupFilter.children = [child] if (nestedMode.value) placeHolderGroupFilter.children = [child]
@ -476,7 +488,7 @@ export function useViewFilters(
const index = filters.value.length - 1 const index = filters.value.length - 1
await saveOrUpdate(filters.value[index], index, true) await saveOrUpdate(filters.value[index], index)
lastFilters.value = clone(filters.value) lastFilters.value = clone(filters.value)

3
packages/nc-gui/store/bases.ts

@ -43,6 +43,8 @@ export const useBases = defineStore('basesStore', () => {
return basesMap return basesMap
}) })
const isDataSourceLimitReached = computed(() => Number(openedProject.value?.sources?.length) > 9)
const workspaceStore = useWorkspace() const workspaceStore = useWorkspace()
const tableStore = useTablesStore() const tableStore = useTablesStore()
@ -389,6 +391,7 @@ export const useBases = defineStore('basesStore', () => {
toggleStarred, toggleStarred,
basesUser, basesUser,
clearBasesUser, clearBasesUser,
isDataSourceLimitReached,
} }
}) })

438
packages/nocodb/src/helpers/webhookHelpers.ts

@ -51,9 +51,6 @@ export async function validateCondition(
for (const _filter of filters) { for (const _filter of filters) {
const filter = _filter instanceof Filter ? _filter : new Filter(_filter); const filter = _filter instanceof Filter ? _filter : new Filter(_filter);
let res; let res;
const column = await filter.getColumn();
const field = column.title;
let val = data[field];
if (filter.is_group) { if (filter.is_group) {
res = await validateCondition( res = await validateCondition(
filter.children || (await filter.getChildren()), filter.children || (await filter.getChildren()),
@ -62,239 +59,244 @@ export async function validateCondition(
client, client,
}, },
); );
} else if ( } else {
[ const column = await filter.getColumn();
UITypes.Date, const field = column.title;
UITypes.DateTime, let val = data[field];
UITypes.CreatedTime, if (
UITypes.LastModifiedTime, [
].includes(column.uidt) && UITypes.Date,
!['empty', 'blank', 'notempty', 'notblank'].includes(filter.comparison_op) UITypes.DateTime,
) { UITypes.CreatedTime,
const dateFormat = UITypes.LastModifiedTime,
client === 'mysql2' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ'; ].includes(column.uidt) &&
!['empty', 'blank', 'notempty', 'notblank'].includes(filter.comparison_op)
let now = dayjs(new Date()); ) {
const dateFormatFromMeta = column?.meta?.date_format; const dateFormat =
const dataVal: any = val; client === 'mysql2' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ';
let filterVal: any = filter.value;
if (dateFormatFromMeta && isDateMonthFormat(dateFormatFromMeta)) { let now = dayjs(new Date());
// reset to 1st const dateFormatFromMeta = column?.meta?.date_format;
now = dayjs(now).date(1); const dataVal: any = val;
if (val) val = dayjs(val).date(1); let filterVal: any = filter.value;
} if (dateFormatFromMeta && isDateMonthFormat(dateFormatFromMeta)) {
if (filterVal) res = dayjs(filterVal).isSame(dataVal, 'day'); // reset to 1st
now = dayjs(now).date(1);
// handle sub operation if (val) val = dayjs(val).date(1);
switch (filter.comparison_sub_op) { }
case 'today': if (filterVal) res = dayjs(filterVal).isSame(dataVal, 'day');
filterVal = now;
break; // handle sub operation
case 'tomorrow': switch (filter.comparison_sub_op) {
filterVal = now.add(1, 'day'); case 'today':
break; filterVal = now;
case 'yesterday': break;
filterVal = now.add(-1, 'day'); case 'tomorrow':
break; filterVal = now.add(1, 'day');
case 'oneWeekAgo': break;
filterVal = now.add(-1, 'week'); case 'yesterday':
break; filterVal = now.add(-1, 'day');
case 'oneWeekFromNow': break;
filterVal = now.add(1, 'week'); case 'oneWeekAgo':
break; filterVal = now.add(-1, 'week');
case 'oneMonthAgo': break;
filterVal = now.add(-1, 'month'); case 'oneWeekFromNow':
break; filterVal = now.add(1, 'week');
case 'oneMonthFromNow': break;
filterVal = now.add(1, 'month'); case 'oneMonthAgo':
break; filterVal = now.add(-1, 'month');
case 'daysAgo': break;
if (!filterVal) return; case 'oneMonthFromNow':
filterVal = now.add(-filterVal, 'day'); filterVal = now.add(1, 'month');
break; break;
case 'daysFromNow': case 'daysAgo':
if (!filterVal) return; if (!filterVal) return;
filterVal = now.add(filterVal, 'day'); filterVal = now.add(-filterVal, 'day');
break; break;
case 'exactDate': case 'daysFromNow':
if (!filterVal) return; if (!filterVal) return;
break; filterVal = now.add(filterVal, 'day');
// sub-ops for `isWithin` comparison break;
case 'pastWeek': case 'exactDate':
filterVal = now.add(-1, 'week'); if (!filterVal) return;
break; break;
case 'pastMonth': // sub-ops for `isWithin` comparison
filterVal = now.add(-1, 'month'); case 'pastWeek':
break; filterVal = now.add(-1, 'week');
case 'pastYear': break;
filterVal = now.add(-1, 'year'); case 'pastMonth':
break; filterVal = now.add(-1, 'month');
case 'nextWeek': break;
filterVal = now.add(1, 'week'); case 'pastYear':
break; filterVal = now.add(-1, 'year');
case 'nextMonth': break;
filterVal = now.add(1, 'month'); case 'nextWeek':
break; filterVal = now.add(1, 'week');
case 'nextYear': break;
filterVal = now.add(1, 'year'); case 'nextMonth':
break; filterVal = now.add(1, 'month');
case 'pastNumberOfDays': break;
if (!filterVal) return; case 'nextYear':
filterVal = now.add(-filterVal, 'day'); filterVal = now.add(1, 'year');
break; break;
case 'nextNumberOfDays': case 'pastNumberOfDays':
if (!filterVal) return; if (!filterVal) return;
filterVal = now.add(filterVal, 'day'); filterVal = now.add(-filterVal, 'day');
break; break;
} case 'nextNumberOfDays':
if (!filterVal) return;
filterVal = now.add(filterVal, 'day');
break;
}
if (dataVal) {
switch (filter.comparison_op) {
case 'eq':
res = dayjs(dataVal).isSame(filterVal, 'day');
break;
case 'neq':
res = !dayjs(dataVal).isSame(filterVal, 'day');
break;
case 'gt':
res = dayjs(dataVal).isAfter(filterVal, 'day');
break;
case 'lt':
res = dayjs(dataVal).isBefore(filterVal, 'day');
break;
case 'lte':
case 'le':
res = dayjs(dataVal).isSameOrBefore(filterVal, 'day');
break;
case 'gte':
case 'ge':
res = dayjs(dataVal).isSameOrAfter(filterVal, 'day');
break;
case 'empty':
case 'blank':
res = dataVal === '' || dataVal === null || dataVal === undefined;
break;
case 'notempty':
case 'notblank':
res = !(
dataVal === '' ||
dataVal === null ||
dataVal === undefined
);
break;
case 'isWithin': {
let now = dayjs(new Date()).format(dateFormat).toString();
now = column.uidt === UITypes.Date ? now.substring(0, 10) : now;
switch (filter.comparison_sub_op) {
case 'pastWeek':
case 'pastMonth':
case 'pastYear':
case 'pastNumberOfDays':
res = dayjs(dataVal).isBetween(filterVal, now, 'day');
break;
case 'nextWeek':
case 'nextMonth':
case 'nextYear':
case 'nextNumberOfDays':
res = dayjs(dataVal).isBetween(now, filterVal, 'day');
break;
}
}
}
}
} else {
switch (typeof filter.value) {
case 'boolean':
val = !!data[field];
break;
case 'number':
val = +data[field];
break;
}
if (dataVal) {
switch (filter.comparison_op) { switch (filter.comparison_op) {
case 'eq': case 'eq':
res = dayjs(dataVal).isSame(filterVal, 'day'); res = val == filter.value;
break; break;
case 'neq': case 'neq':
res = !dayjs(dataVal).isSame(filterVal, 'day'); res = val != filter.value;
break; break;
case 'gt': case 'like':
res = dayjs(dataVal).isAfter(filterVal, 'day'); res =
data[field]
?.toString?.()
?.toLowerCase()
?.indexOf(filter.value?.toLowerCase()) > -1;
break; break;
case 'lt': case 'nlike':
res = dayjs(dataVal).isBefore(filterVal, 'day'); res =
break; data[field]
case 'lte': ?.toString?.()
case 'le': ?.toLowerCase()
res = dayjs(dataVal).isSameOrBefore(filterVal, 'day'); ?.indexOf(filter.value?.toLowerCase()) === -1;
break;
case 'gte':
case 'ge':
res = dayjs(dataVal).isSameOrAfter(filterVal, 'day');
break; break;
case 'empty': case 'empty':
case 'blank': case 'blank':
res = dataVal === '' || dataVal === null || dataVal === undefined; res =
data[field] === '' ||
data[field] === null ||
data[field] === undefined;
break; break;
case 'notempty': case 'notempty':
case 'notblank': case 'notblank':
res = !( res = !(
dataVal === '' || data[field] === '' ||
dataVal === null || data[field] === null ||
dataVal === undefined data[field] === undefined
); );
break; break;
case 'isWithin': { case 'checked':
let now = dayjs(new Date()).format(dateFormat).toString(); res = !!data[field];
now = column.uidt === UITypes.Date ? now.substring(0, 10) : now; break;
switch (filter.comparison_sub_op) { case 'notchecked':
case 'pastWeek': res = !data[field];
case 'pastMonth': break;
case 'pastYear': case 'null':
case 'pastNumberOfDays': res = res = data[field] === null;
res = dayjs(dataVal).isBetween(filterVal, now, 'day'); break;
break; case 'notnull':
case 'nextWeek': res = data[field] !== null;
case 'nextMonth': break;
case 'nextYear': case 'allof':
case 'nextNumberOfDays': res = (
res = dayjs(dataVal).isBetween(now, filterVal, 'day'); filter.value?.split(',').map((item) => item.trim()) ?? []
break; ).every((item) => (data[field]?.split(',') ?? []).includes(item));
} break;
} case 'anyof':
res = (
filter.value?.split(',').map((item) => item.trim()) ?? []
).some((item) => (data[field]?.split(',') ?? []).includes(item));
break;
case 'nallof':
res = !(
filter.value?.split(',').map((item) => item.trim()) ?? []
).every((item) => (data[field]?.split(',') ?? []).includes(item));
break;
case 'nanyof':
res = !(
filter.value?.split(',').map((item) => item.trim()) ?? []
).some((item) => (data[field]?.split(',') ?? []).includes(item));
break;
case 'lt':
res = +data[field] < +filter.value;
break;
case 'lte':
case 'le':
res = +data[field] <= +filter.value;
break;
case 'gt':
res = +data[field] > +filter.value;
break;
case 'gte':
case 'ge':
res = +data[field] >= +filter.value;
break;
} }
} }
} else {
switch (typeof filter.value) {
case 'boolean':
val = !!data[field];
break;
case 'number':
val = +data[field];
break;
}
switch (filter.comparison_op) {
case 'eq':
res = val == filter.value;
break;
case 'neq':
res = val != filter.value;
break;
case 'like':
res =
data[field]
?.toString?.()
?.toLowerCase()
?.indexOf(filter.value?.toLowerCase()) > -1;
break;
case 'nlike':
res =
data[field]
?.toString?.()
?.toLowerCase()
?.indexOf(filter.value?.toLowerCase()) === -1;
break;
case 'empty':
case 'blank':
res =
data[field] === '' ||
data[field] === null ||
data[field] === undefined;
break;
case 'notempty':
case 'notblank':
res = !(
data[field] === '' ||
data[field] === null ||
data[field] === undefined
);
break;
case 'checked':
res = !!data[field];
break;
case 'notchecked':
res = !data[field];
break;
case 'null':
res = res = data[field] === null;
break;
case 'notnull':
res = data[field] !== null;
break;
case 'allof':
res = (
filter.value?.split(',').map((item) => item.trim()) ?? []
).every((item) => (data[field]?.split(',') ?? []).includes(item));
break;
case 'anyof':
res = (
filter.value?.split(',').map((item) => item.trim()) ?? []
).some((item) => (data[field]?.split(',') ?? []).includes(item));
break;
case 'nallof':
res = !(
filter.value?.split(',').map((item) => item.trim()) ?? []
).every((item) => (data[field]?.split(',') ?? []).includes(item));
break;
case 'nanyof':
res = !(
filter.value?.split(',').map((item) => item.trim()) ?? []
).some((item) => (data[field]?.split(',') ?? []).includes(item));
break;
case 'lt':
res = +data[field] < +filter.value;
break;
case 'lte':
case 'le':
res = +data[field] <= +filter.value;
break;
case 'gt':
res = +data[field] > +filter.value;
break;
case 'gte':
case 'ge':
res = +data[field] >= +filter.value;
break;
}
} }
switch (filter.logical_op) { switch (filter.logical_op) {

4
packages/nocodb/src/models/Filter.ts

@ -515,7 +515,9 @@ export default class Filter implements FilterType {
}); });
await NocoCache.setList(CacheScope.FILTER_EXP, [hookId], filterObjs); await NocoCache.setList(CacheScope.FILTER_EXP, [hookId], filterObjs);
} }
return filterObjs?.map((f) => this.castType(f)); return filterObjs
?.filter((f) => !f.fk_parent_id)
?.map((f) => this.castType(f));
} }
static async parentFilterList( static async parentFilterList(

Loading…
Cancel
Save