|
|
@ -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) { |
|
|
|