Browse Source

Merge pull request #7574 from nocodb/nc-feat/formula-imp

Nc feat/formula imp
pull/7577/head
Raju Udava 10 months ago committed by GitHub
parent
commit
00fd48fc5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 36
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
  2. 18
      packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
  3. 23
      packages/nc-gui/components/virtual-cell/Formula.vue
  4. 33
      packages/nc-gui/composables/useViewFilters.ts
  5. 53
      packages/nc-gui/utils/filterUtils.ts
  6. 17
      packages/nocodb-sdk/src/lib/UITypes.ts
  7. 1
      packages/nocodb-sdk/src/lib/index.ts
  8. 6
      packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts
  9. 1
      packages/nocodb/src/db/BaseModelSqlv2.ts
  10. 82
      packages/nocodb/src/db/conditionV2.ts
  11. 4
      packages/nocodb/src/db/sortV2.ts

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

@ -76,6 +76,7 @@ const {
isComparisonSubOpAllowed, isComparisonSubOpAllowed,
loadBtLookupTypes, loadBtLookupTypes,
btLookupTypesMap, btLookupTypesMap,
types,
} = useViewFilters( } = useViewFilters(
activeView, activeView,
parentId?.value, parentId?.value,
@ -114,8 +115,9 @@ const isFilterDraft = (filter: Filter, col: ColumnType) => {
} }
if ( if (
comparisonOpList(col.uidt as UITypes, col?.meta?.date_format).find((compOp) => compOp.value === filter.comparison_op) comparisonOpList(types.value[col.id] as UITypes, col?.meta?.date_format).find(
?.ignoreVal (compOp) => compOp.value === filter.comparison_op,
)?.ignoreVal
) { ) {
return false return false
} }
@ -143,7 +145,7 @@ const filterUpdateCondition = (filter: FilterType, i: number) => {
// hence remove the previous value // hence remove the previous value
filter.value = null filter.value = null
filter.comparison_sub_op = null filter.comparison_sub_op = null
} else if (isDateType(col.uidt as UITypes)) { } else if (isDateType(types.value[col.id] as UITypes)) {
// for date / datetime, // for date / datetime,
// the input type could be decimal or datepicker / datetime picker // the input type could be decimal or datepicker / datetime picker
// hence remove the previous value // hence remove the previous value
@ -173,17 +175,6 @@ const filterUpdateCondition = (filter: FilterType, i: number) => {
}) })
} }
const types = computed(() => {
if (!meta.value?.columns?.length) {
return {}
}
return meta.value?.columns?.reduce((obj: any, col: any) => {
obj[col.id] = col.uidt
return obj
}, {})
})
watch( watch(
() => activeView.value?.id, () => activeView.value?.id,
(n, o) => { (n, o) => {
@ -237,11 +228,11 @@ const selectFilterField = (filter: Filter, index: number) => {
// since the existing one may not be supported for the new field // since the existing one may not be supported for the new field
// e.g. `eq` operator is not supported in checkbox field // e.g. `eq` operator is not supported in checkbox field
// hence, get the first option of the supported operators of the new field // hence, get the first option of the supported operators of the new field
filter.comparison_op = comparisonOpList(col.uidt as UITypes, col?.meta?.date_format).find((compOp) => filter.comparison_op = comparisonOpList(types.value[col.id] as UITypes, col?.meta?.date_format).find((compOp) =>
isComparisonOpAllowed(filter, compOp), isComparisonOpAllowed(filter, compOp),
)?.value as FilterType['comparison_op'] )?.value as FilterType['comparison_op']
if (isDateType(col.uidt as UITypes) && !['blank', 'notblank'].includes(filter.comparison_op!)) { if (isDateType(types.value[col.id] as UITypes) && !['blank', 'notblank'].includes(filter.comparison_op!)) {
if (filter.comparison_op === 'isWithin') { if (filter.comparison_op === 'isWithin') {
filter.comparison_sub_op = 'pastNumberOfDays' filter.comparison_sub_op = 'pastNumberOfDays'
} else { } else {
@ -319,8 +310,9 @@ const showFilterInput = (filter: Filter) => {
(op) => op.value === filter.comparison_sub_op, (op) => op.value === filter.comparison_sub_op,
)?.ignoreVal )?.ignoreVal
} else { } else {
return !comparisonOpList(col?.uidt as UITypes, col?.meta?.date_format).find((op) => op.value === filter.comparison_op) return !comparisonOpList(types.value[col?.id] as UITypes, col?.meta?.date_format).find(
?.ignoreVal (op) => op.value === filter.comparison_op,
)?.ignoreVal
} }
} }
@ -462,7 +454,7 @@ function isDateType(uidt: UITypes) {
@change="filterUpdateCondition(filter, i)" @change="filterUpdateCondition(filter, i)"
> >
<template <template
v-for="compOp of comparisonOpList(getColumn(filter)?.uidt, getColumn(filter)?.meta?.date_format)" v-for="compOp of comparisonOpList(types[filter.fk_column_id], getColumn(filter)?.meta?.date_format)"
:key="compOp.value" :key="compOp.value"
> >
<a-select-option v-if="isComparisonOpAllowed(filter, compOp)" :value="compOp.value"> <a-select-option v-if="isComparisonOpAllowed(filter, compOp)" :value="compOp.value">
@ -481,7 +473,7 @@ function isDateType(uidt: UITypes) {
<div v-if="['blank', 'notblank'].includes(filter.comparison_op)" class="flex flex-grow"></div> <div v-if="['blank', 'notblank'].includes(filter.comparison_op)" class="flex flex-grow"></div>
<NcSelect <NcSelect
v-else-if="isDateType(getColumn(filter)?.uidt)" v-else-if="isDateType(types[filter.fk_column_id])"
v-model:value="filter.comparison_sub_op" v-model:value="filter.comparison_sub_op"
v-e="['c:filter:sub-comparison-op:select']" v-e="['c:filter:sub-comparison-op:select']"
:dropdown-match-select-width="false" :dropdown-match-select-width="false"
@ -529,12 +521,12 @@ function isDateType(uidt: UITypes) {
<SmartsheetToolbarFilterInput <SmartsheetToolbarFilterInput
v-if="showFilterInput(filter)" v-if="showFilterInput(filter)"
class="nc-filter-value-select rounded-md min-w-34" class="nc-filter-value-select rounded-md min-w-34"
:column="getColumn(filter)" :column="{ ...getColumn(filter), uidt: types[filter.fk_column_id] }"
:filter="filter" :filter="filter"
@update-filter-value="(value) => updateFilterValue(value, filter, i)" @update-filter-value="(value) => updateFilterValue(value, filter, i)"
@click.stop @click.stop
/> />
<div v-else-if="!isDateType(getColumn(filter)?.uidt)" class="flex-grow"></div> <div v-else-if="!isDateType(types[filter.fk_column_id])" class="flex-grow"></div>
<NcButton <NcButton
v-if="!filter.readOnly" v-if="!filter.readOnly"

18
packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { PlanLimitTypes, RelationTypes, UITypes, isLinksOrLTAR, isSystemColumn } from 'nocodb-sdk' import { PlanLimitTypes, RelationTypes, UITypes, getEquivalentUIType, isLinksOrLTAR, isSystemColumn } from 'nocodb-sdk'
import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk' import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk'
import { import {
ActiveViewInj, ActiveViewInj,
@ -75,8 +75,20 @@ const availableColumns = computed(() => {
}) })
const getColumnUidtByID = (key?: string) => { const getColumnUidtByID = (key?: string) => {
if (!key) return '' if (!key || !columnByID.value[key]) return ''
return columnByID.value[key]?.uidt || ''
const column = columnByID.value[key]
let uidt = column.uidt
if (column.uidt === UITypes.Formula) {
uidt =
getEquivalentUIType({
formulaColumn: column,
}) || uidt
}
return uidt || ''
} }
const open = ref(false) const open = ref(false)

23
packages/nc-gui/components/virtual-cell/Formula.vue

@ -1,8 +1,19 @@
<script lang="ts" setup> <script lang="ts" setup>
import { handleTZ } from 'nocodb-sdk' import { FormulaDataTypes, handleTZ } from 'nocodb-sdk'
import type { ColumnType } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { CellValueInj, ColumnInj, computed, inject, renderValue, replaceUrlsWithLink, useBase } from '#imports' import {
CellValueInj,
ColumnInj,
IsExpandedFormOpenInj,
computed,
inject,
ref,
renderValue,
replaceUrlsWithLink,
useBase,
useShowNotEditableWarning,
} from '#imports'
// todo: column type doesn't have required property `error` - throws in typecheck // todo: column type doesn't have required property `error` - throws in typecheck
const column = inject(ColumnInj) as Ref<ColumnType & { colOptions: { error: any } }> const column = inject(ColumnInj) as Ref<ColumnType & { colOptions: { error: any } }>
@ -19,10 +30,16 @@ const urls = computed(() => replaceUrlsWithLink(result.value))
const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning, activateShowEditNonEditableFieldWarning } = const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning, activateShowEditNonEditableFieldWarning } =
useShowNotEditableWarning() useShowNotEditableWarning()
const isNumber = computed(() => (column.value.colOptions as any)?.parsed_tree?.dataType === FormulaDataTypes.NUMERIC)
const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))
const isGrid = inject(IsGridInj, ref(false))
</script> </script>
<template> <template>
<div> <div class="w-full" :class="{ 'text-right': isNumber && isGrid && !isExpandedFormOpen }">
<a-tooltip v-if="column && column.colOptions && column.colOptions.error" placement="bottom" class="text-orange-700"> <a-tooltip v-if="column && column.colOptions && column.colOptions.error" placement="bottom" class="text-orange-700">
<template #title> <template #title>
<span class="font-bold">{{ column.colOptions.error }}</span> <span class="font-bold">{{ column.colOptions.error }}</span>

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

@ -1,4 +1,13 @@
import type { ColumnType, FilterType, LinkToAnotherRecordType, LookupType, ViewType } from 'nocodb-sdk' import {
type ColumnType,
type FilterType,
FormulaDataTypes,
type FormulaType,
type LinkToAnotherRecordType,
type LookupType,
type ViewType,
getEquivalentUIType,
} from 'nocodb-sdk'
import type { ComputedRef, Ref } from 'vue' import type { ComputedRef, Ref } from 'vue'
import type { SelectProps } from 'ant-design-vue' import type { SelectProps } from 'ant-design-vue'
import { UITypes, isSystemColumn } from 'nocodb-sdk' import { UITypes, isSystemColumn } from 'nocodb-sdk'
@ -103,8 +112,15 @@ export function useViewFilters(
} }
return meta.value?.columns?.reduce((obj: any, col: any) => { return meta.value?.columns?.reduce((obj: any, col: any) => {
if (col.uidt === UITypes.Formula) {
const formulaUIType = getEquivalentUIType({
formulaColumn: col,
})
obj[col.id] = formulaUIType || col.uidt
}
// if column is a lookup column, then use the lookup type extracted from the column // if column is a lookup column, then use the lookup type extracted from the column
if (btLookupTypesMap.value[col.id]) { else if (btLookupTypesMap.value[col.id]) {
obj[col.id] = btLookupTypesMap.value[col.id].uidt obj[col.id] = btLookupTypesMap.value[col.id].uidt
} else { } else {
obj[col.id] = col.uidt obj[col.id] = col.uidt
@ -137,9 +153,11 @@ export function useViewFilters(
}, },
) => { ) => {
const isNullOrEmptyOp = ['empty', 'notempty', 'null', 'notnull'].includes(compOp.value) const isNullOrEmptyOp = ['empty', 'notempty', 'null', 'notnull'].includes(compOp.value)
const uidt = types.value[filter.fk_column_id]
if (compOp.includedTypes) { if (compOp.includedTypes) {
// include allowed values only if selected column type matches // include allowed values only if selected column type matches
if (filter.fk_column_id && compOp.includedTypes.includes(types.value[filter.fk_column_id])) { if (filter.fk_column_id && compOp.includedTypes.includes(uidt)) {
// for 'empty', 'notempty', 'null', 'notnull', // for 'empty', 'notempty', 'null', 'notnull',
// show them based on `showNullAndEmptyInFilter` in Base Settings // show them based on `showNullAndEmptyInFilter` in Base Settings
return isNullOrEmptyOp ? baseMeta.value.showNullAndEmptyInFilter : true return isNullOrEmptyOp ? baseMeta.value.showNullAndEmptyInFilter : true
@ -148,7 +166,7 @@ export function useViewFilters(
} }
} else if (compOp.excludedTypes) { } else if (compOp.excludedTypes) {
// include not allowed values only if selected column type not matches // include not allowed values only if selected column type not matches
if (filter.fk_column_id && !compOp.excludedTypes.includes(types.value[filter.fk_column_id])) { if (filter.fk_column_id && !compOp.excludedTypes.includes(uidt)) {
// for 'empty', 'notempty', 'null', 'notnull', // for 'empty', 'notempty', 'null', 'notnull',
// show them based on `showNullAndEmptyInFilter` in Base Settings // show them based on `showNullAndEmptyInFilter` in Base Settings
return isNullOrEmptyOp ? baseMeta.value.showNullAndEmptyInFilter : true return isNullOrEmptyOp ? baseMeta.value.showNullAndEmptyInFilter : true
@ -170,12 +188,14 @@ export function useViewFilters(
excludedTypes?: UITypes[] excludedTypes?: UITypes[]
}, },
) => { ) => {
const uidt = types.value[filter.fk_column_id]
if (compOp.includedTypes) { if (compOp.includedTypes) {
// include allowed values only if selected column type matches // include allowed values only if selected column type matches
return filter.fk_column_id && compOp.includedTypes.includes(types.value[filter.fk_column_id]) return filter.fk_column_id && compOp.includedTypes.includes(uidt)
} else if (compOp.excludedTypes) { } else if (compOp.excludedTypes) {
// include not allowed values only if selected column type not matches // include not allowed values only if selected column type not matches
return filter.fk_column_id && !compOp.excludedTypes.includes(types.value[filter.fk_column_id]) return filter.fk_column_id && !compOp.excludedTypes.includes(uidt)
} }
} }
@ -479,5 +499,6 @@ export function useViewFilters(
isComparisonSubOpAllowed, isComparisonSubOpAllowed,
loadBtLookupTypes, loadBtLookupTypes,
btLookupTypesMap, btLookupTypesMap,
types
} }
} }

53
packages/nc-gui/utils/filterUtils.ts

@ -4,9 +4,15 @@ const getEqText = (fieldUiType: UITypes) => {
if (isNumericCol(fieldUiType) || fieldUiType === UITypes.Time) { if (isNumericCol(fieldUiType) || fieldUiType === UITypes.Time) {
return '=' return '='
} else if ( } else if (
[UITypes.SingleSelect, UITypes.Collaborator, UITypes.LinkToAnotherRecord, UITypes.Date, UITypes.DateTime].includes( [
fieldUiType, UITypes.SingleSelect,
) UITypes.Collaborator,
UITypes.LinkToAnotherRecord,
UITypes.Date,
UITypes.CreatedTime,
UITypes.LastModifiedTime,
UITypes.DateTime,
].includes(fieldUiType)
) { ) {
return 'is' return 'is'
} }
@ -17,9 +23,15 @@ const getNeqText = (fieldUiType: UITypes) => {
if (isNumericCol(fieldUiType) || fieldUiType === UITypes.Time) { if (isNumericCol(fieldUiType) || fieldUiType === UITypes.Time) {
return '!=' return '!='
} else if ( } else if (
[UITypes.SingleSelect, UITypes.Collaborator, UITypes.LinkToAnotherRecord, UITypes.Date, UITypes.DateTime].includes( [
fieldUiType, UITypes.SingleSelect,
) UITypes.Collaborator,
UITypes.LinkToAnotherRecord,
UITypes.Date,
UITypes.CreatedTime,
UITypes.LastModifiedTime,
UITypes.DateTime,
].includes(fieldUiType)
) { ) {
return 'is not' return 'is not'
} }
@ -41,28 +53,28 @@ const getNotLikeText = (fieldUiType: UITypes) => {
} }
const getGtText = (fieldUiType: UITypes) => { const getGtText = (fieldUiType: UITypes) => {
if ([UITypes.Date, UITypes.DateTime].includes(fieldUiType)) { if ([UITypes.Date, UITypes.DateTime, UITypes.CreatedTime, UITypes.LastModifiedTime].includes(fieldUiType)) {
return 'is after' return 'is after'
} }
return '>' return '>'
} }
const getLtText = (fieldUiType: UITypes) => { const getLtText = (fieldUiType: UITypes) => {
if ([UITypes.Date, UITypes.DateTime].includes(fieldUiType)) { if ([UITypes.Date, UITypes.DateTime, UITypes.CreatedTime, UITypes.LastModifiedTime].includes(fieldUiType)) {
return 'is before' return 'is before'
} }
return '<' return '<'
} }
const getGteText = (fieldUiType: UITypes) => { const getGteText = (fieldUiType: UITypes) => {
if ([UITypes.Date, UITypes.DateTime].includes(fieldUiType)) { if ([UITypes.Date, UITypes.DateTime, UITypes.CreatedTime, UITypes.LastModifiedTime].includes(fieldUiType)) {
return 'is on or after' return 'is on or after'
} }
return '>=' return '>='
} }
const getLteText = (fieldUiType: UITypes) => { const getLteText = (fieldUiType: UITypes) => {
if ([UITypes.Date, UITypes.DateTime].includes(fieldUiType)) { if ([UITypes.Date, UITypes.DateTime, UITypes.CreatedTime, UITypes.LastModifiedTime].includes(fieldUiType)) {
return 'is on or before' return 'is on or before'
} }
return '<=' return '<='
@ -131,6 +143,8 @@ export const comparisonOpList = (
UITypes.Collaborator, UITypes.Collaborator,
UITypes.Date, UITypes.Date,
UITypes.DateTime, UITypes.DateTime,
UITypes.CreatedTime,
UITypes.LastModifiedTime,
UITypes.Time, UITypes.Time,
...numericUITypes, ...numericUITypes,
], ],
@ -149,6 +163,8 @@ export const comparisonOpList = (
UITypes.Collaborator, UITypes.Collaborator,
UITypes.Date, UITypes.Date,
UITypes.DateTime, UITypes.DateTime,
UITypes.CreatedTime,
UITypes.LastModifiedTime,
UITypes.Time, UITypes.Time,
...numericUITypes, ...numericUITypes,
], ],
@ -170,6 +186,8 @@ export const comparisonOpList = (
UITypes.Lookup, UITypes.Lookup,
UITypes.Date, UITypes.Date,
UITypes.DateTime, UITypes.DateTime,
UITypes.CreatedTime,
UITypes.LastModifiedTime,
UITypes.Time, UITypes.Time,
...numericUITypes, ...numericUITypes,
], ],
@ -191,6 +209,8 @@ export const comparisonOpList = (
UITypes.Lookup, UITypes.Lookup,
UITypes.Date, UITypes.Date,
UITypes.DateTime, UITypes.DateTime,
UITypes.CreatedTime,
UITypes.LastModifiedTime,
UITypes.Time, UITypes.Time,
...numericUITypes, ...numericUITypes,
], ],
@ -213,6 +233,8 @@ export const comparisonOpList = (
UITypes.Lookup, UITypes.Lookup,
UITypes.Date, UITypes.Date,
UITypes.DateTime, UITypes.DateTime,
UITypes.CreatedTime,
UITypes.LastModifiedTime,
UITypes.Time, UITypes.Time,
], ],
}, },
@ -234,6 +256,8 @@ export const comparisonOpList = (
UITypes.Lookup, UITypes.Lookup,
UITypes.Date, UITypes.Date,
UITypes.DateTime, UITypes.DateTime,
UITypes.CreatedTime,
UITypes.LastModifiedTime,
UITypes.Time, UITypes.Time,
], ],
}, },
@ -304,7 +328,14 @@ export const comparisonOpList = (
text: getLteText(fieldUiType), text: getLteText(fieldUiType),
value: 'lte', value: 'lte',
ignoreVal: false, ignoreVal: false,
includedTypes: [...numericUITypes, UITypes.Date, UITypes.DateTime, UITypes.Time], includedTypes: [
...numericUITypes,
UITypes.Date,
UITypes.DateTime,
UITypes.Time,
UITypes.CreatedTime,
UITypes.LastModifiedTime,
],
}, },
{ {
text: 'is within', text: 'is within',

17
packages/nocodb-sdk/src/lib/UITypes.ts

@ -1,4 +1,5 @@
import { ColumnReqType, ColumnType } from './Api'; import { ColumnReqType, ColumnType } from './Api';
import { FormulaDataTypes } from './formulaHelpers';
enum UITypes { enum UITypes {
ID = 'ID', ID = 'ID',
@ -185,4 +186,20 @@ export function isLinksOrLTAR(
); );
} }
export const getEquivalentUIType = ({
formulaColumn,
}: {
formulaColumn: ColumnType;
}): void | UITypes => {
switch ((formulaColumn?.colOptions as any)?.parsed_tree?.dataType) {
case FormulaDataTypes.NUMERIC:
return UITypes.Number;
case FormulaDataTypes.DATE:
return UITypes.DateTime;
case FormulaDataTypes.LOGICAL:
case FormulaDataTypes.BOOLEAN:
return UITypes.Checkbox;
}
};
export default UITypes; export default UITypes;

1
packages/nocodb-sdk/src/lib/index.ts

@ -17,6 +17,7 @@ export {
isCreatedOrLastModifiedTimeCol, isCreatedOrLastModifiedTimeCol,
isCreatedOrLastModifiedByCol, isCreatedOrLastModifiedByCol,
isHiddenCol, isHiddenCol,
getEquivalentUIType,
} from '~/lib/UITypes'; } from '~/lib/UITypes';
export { default as CustomAPI, FileType } from '~/lib/CustomAPI'; export { default as CustomAPI, FileType } from '~/lib/CustomAPI';
export { default as TemplateGenerator } from '~/lib/TemplateGenerator'; export { default as TemplateGenerator } from '~/lib/TemplateGenerator';

6
packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts

@ -915,10 +915,6 @@ export class SqliteUi {
static getUnsupportedFnList() { static getUnsupportedFnList() {
return [ return [
'LOG',
'EXP',
'POWER',
'SQRT',
'XOR', 'XOR',
'REGEX_MATCH', 'REGEX_MATCH',
'REGEX_EXTRACT', 'REGEX_EXTRACT',
@ -926,8 +922,6 @@ export class SqliteUi {
'VALUE', 'VALUE',
'COUNTA', 'COUNTA',
'COUNT', 'COUNT',
'ROUNDDOWN',
'ROUNDUP',
'DATESTR', 'DATESTR',
'DAY', 'DAY',
'MONTH', 'MONTH',

1
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -427,7 +427,6 @@ class BaseModelSqlv2 {
const proto = await this.getProto(); const proto = await this.getProto();
let data; let data;
try { try {
data = await this.execAndParse(qb); data = await this.execAndParse(qb);
} catch (e) { } catch (e) {

82
packages/nocodb/src/db/conditionV2.ts

@ -1,5 +1,6 @@
import { import {
FormulaDataTypes, FormulaDataTypes,
getEquivalentUIType,
isDateMonthFormat, isDateMonthFormat,
isNumericCol, isNumericCol,
RelationTypes, RelationTypes,
@ -568,12 +569,18 @@ const parseConditionV2 = async (
return (qb: Knex.QueryBuilder) => { return (qb: Knex.QueryBuilder) => {
let [field, val] = [_field, _val]; let [field, val] = [_field, _val];
// based on custom where clause(builder), we need to change the field and val
// todo: refactor this to use a better approach to make it more readable and clean
let genVal = customWhereClause ? field : val;
const dateFormat = const dateFormat =
qb?.client?.config?.client === 'mysql2' qb?.client?.config?.client === 'mysql2'
? 'YYYY-MM-DD HH:mm:ss' ? 'YYYY-MM-DD HH:mm:ss'
: 'YYYY-MM-DD HH:mm:ssZ'; : 'YYYY-MM-DD HH:mm:ssZ';
if ( if (
(column.uidt === UITypes.Formula &&
getEquivalentUIType({ formulaColumn: column }) ==
UITypes.DateTime) ||
[ [
UITypes.Date, UITypes.Date,
UITypes.DateTime, UITypes.DateTime,
@ -586,82 +593,91 @@ const parseConditionV2 = async (
if (dateFormatFromMeta && isDateMonthFormat(dateFormatFromMeta)) { if (dateFormatFromMeta && isDateMonthFormat(dateFormatFromMeta)) {
// reset to 1st // reset to 1st
now = dayjs(now).date(1); now = dayjs(now).date(1);
if (val) val = dayjs(val).date(1); if (val) genVal = dayjs(val).date(1);
} }
// handle sub operation // handle sub operation
switch (filter.comparison_sub_op) { switch (filter.comparison_sub_op) {
case 'today': case 'today':
val = now; genVal = now;
break; break;
case 'tomorrow': case 'tomorrow':
val = now.add(1, 'day'); genVal = now.add(1, 'day');
break; break;
case 'yesterday': case 'yesterday':
val = now.add(-1, 'day'); genVal = now.add(-1, 'day');
break; break;
case 'oneWeekAgo': case 'oneWeekAgo':
val = now.add(-1, 'week'); genVal = now.add(-1, 'week');
break; break;
case 'oneWeekFromNow': case 'oneWeekFromNow':
val = now.add(1, 'week'); genVal = now.add(1, 'week');
break; break;
case 'oneMonthAgo': case 'oneMonthAgo':
val = now.add(-1, 'month'); genVal = now.add(-1, 'month');
break; break;
case 'oneMonthFromNow': case 'oneMonthFromNow':
val = now.add(1, 'month'); genVal = now.add(1, 'month');
break; break;
case 'daysAgo': case 'daysAgo':
if (!val) return; if (!val) return;
val = now.add(-val, 'day'); genVal = now.add(-genVal, 'day');
break; break;
case 'daysFromNow': case 'daysFromNow':
if (!val) return; if (!val) return;
val = now.add(val, 'day'); genVal = now.add(genVal, 'day');
break; break;
case 'exactDate': case 'exactDate':
if (!val) return; if (!genVal) return;
break; break;
// sub-ops for `isWithin` comparison // sub-ops for `isWithin` comparison
case 'pastWeek': case 'pastWeek':
val = now.add(-1, 'week'); genVal = now.add(-1, 'week');
break; break;
case 'pastMonth': case 'pastMonth':
val = now.add(-1, 'month'); genVal = now.add(-1, 'month');
break; break;
case 'pastYear': case 'pastYear':
val = now.add(-1, 'year'); genVal = now.add(-1, 'year');
break; break;
case 'nextWeek': case 'nextWeek':
val = now.add(1, 'week'); genVal = now.add(1, 'week');
break; break;
case 'nextMonth': case 'nextMonth':
val = now.add(1, 'month'); genVal = now.add(1, 'month');
break; break;
case 'nextYear': case 'nextYear':
val = now.add(1, 'year'); genVal = now.add(1, 'year');
break; break;
case 'pastNumberOfDays': case 'pastNumberOfDays':
if (!val) return; if (!val) return;
val = now.add(-val, 'day'); genVal = now.add(-genVal, 'day');
break; break;
case 'nextNumberOfDays': case 'nextNumberOfDays':
if (!val) return; if (!genVal) return;
val = now.add(val, 'day'); genVal = now.add(genVal, 'day');
break; break;
} }
if (dayjs.isDayjs(val)) { if (dayjs.isDayjs(genVal)) {
// turn `val` in dayjs object format to string // turn `val` in dayjs object format to string
val = val.format(dateFormat).toString(); genVal = genVal.format(dateFormat).toString();
// keep YYYY-MM-DD only for date // keep YYYY-MM-DD only for date
val = column.uidt === UITypes.Date ? val.substring(0, 10) : val; genVal =
column.uidt === UITypes.Date ? genVal.substring(0, 10) : genVal;
} }
} }
if (isNumericCol(column.uidt) && typeof val === 'string') { if (isNumericCol(column.uidt) && typeof genVal === 'string') {
// convert to number // convert to number
val = +val; genVal = +genVal;
}
// if customWhereClause(builder) is provided, replace field with raw value
// or assign value to val
if (customWhereClause) {
field = knex.raw('?', [genVal]);
} else {
val = genVal;
} }
switch (filter.comparison_op) { switch (filter.comparison_op) {
@ -681,6 +697,9 @@ const parseConditionV2 = async (
) { ) {
qb = qb.where(field, val); qb = qb.where(field, val);
} else if ( } else if (
(column.uidt === UITypes.Formula &&
getEquivalentUIType({ formulaColumn: column }) ==
UITypes.DateTime) ||
column.ct === 'timestamp' || column.ct === 'timestamp' ||
column.ct === 'date' || column.ct === 'date' ||
column.ct === 'datetime' column.ct === 'datetime'
@ -692,6 +711,9 @@ const parseConditionV2 = async (
} }
} else { } else {
if ( if (
(column.uidt === UITypes.Formula &&
getEquivalentUIType({ formulaColumn: column }) ==
UITypes.DateTime) ||
[ [
UITypes.DateTime, UITypes.DateTime,
UITypes.CreatedTime, UITypes.CreatedTime,
@ -1038,18 +1060,24 @@ const parseConditionV2 = async (
case 'isWithin': { case 'isWithin': {
let now = dayjs(new Date()).format(dateFormat).toString(); let now = dayjs(new Date()).format(dateFormat).toString();
now = column.uidt === UITypes.Date ? now.substring(0, 10) : now; now = column.uidt === UITypes.Date ? now.substring(0, 10) : now;
// switch between arg based on customWhereClause(builder)
const [firstArg, rangeArg] = [
customWhereClause ? val : field,
customWhereClause ? field : val,
];
switch (filter.comparison_sub_op) { switch (filter.comparison_sub_op) {
case 'pastWeek': case 'pastWeek':
case 'pastMonth': case 'pastMonth':
case 'pastYear': case 'pastYear':
case 'pastNumberOfDays': case 'pastNumberOfDays':
qb = qb.whereBetween(field, [val, now]); qb = qb.whereBetween(firstArg, [rangeArg, now]);
break; break;
case 'nextWeek': case 'nextWeek':
case 'nextMonth': case 'nextMonth':
case 'nextYear': case 'nextYear':
case 'nextNumberOfDays': case 'nextNumberOfDays':
qb = qb.whereBetween(field, [now, val]); qb = qb.whereBetween(firstArg, [now, rangeArg]);
break; break;
} }
} }

4
packages/nocodb/src/db/sortV2.ts

@ -65,9 +65,11 @@ export default async function sortV2(
( (
await column.getColOptions<FormulaColumn>() await column.getColOptions<FormulaColumn>()
).formula, ).formula,
alias, null,
model, model,
column, column,
{},
alias
) )
).builder; ).builder;
qb.orderBy(builder, sort.direction || 'asc', nulls); qb.orderBy(builder, sort.direction || 'asc', nulls);

Loading…
Cancel
Save