Browse Source

Merge pull request #7054 from nocodb/fix/misc-oss

Miscellaneous bug fixes/enhancements
pull/7076/head
Raju Udava 1 year ago committed by GitHub
parent
commit
1f61a15874
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      packages/nc-gui/components/cell/MultiSelect.vue
  2. 1
      packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
  3. 4
      packages/nc-gui/components/smartsheet/column/SelectOptions.vue
  4. 8
      packages/nc-gui/components/virtual-cell/Lookup.vue
  5. 3
      packages/nc-gui/components/virtual-cell/QrCode.vue
  6. 2
      packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts
  7. 2
      packages/nocodb/src/db/BaseModelSqlv2.ts
  8. 22
      packages/nocodb/src/db/generateLookupSelectQuery.ts
  9. 172
      packages/nocodb/src/db/sortV2.ts

13
packages/nc-gui/components/cell/MultiSelect.vue

@ -132,9 +132,13 @@ const vModel = computed({
const selectedTitles = computed(() => const selectedTitles = computed(() =>
modelValue modelValue
? typeof modelValue === 'string' ? Array.isArray(modelValue)
? isMysql(column.value.source_id) ? modelValue
? modelValue.split(',').sort((a, b) => { : isMysql(column.value.source_id)
? modelValue
.toString()
.split(',')
.sort((a, b) => {
const opa = options.value.find((el) => el.title === a) const opa = options.value.find((el) => el.title === a)
const opb = options.value.find((el) => el.title === b) const opb = options.value.find((el) => el.title === b)
if (opa && opb) { if (opa && opb) {
@ -142,8 +146,7 @@ const selectedTitles = computed(() =>
} }
return 0 return 0
}) })
: modelValue.split(',') : modelValue.toString().split(',')
: modelValue
: [], : [],
) )

1
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue

@ -306,6 +306,7 @@ if (props.fromTableExplorer) {
<LazySmartsheetColumnRollupOptions v-if="formState.uidt === UITypes.Rollup" v-model:value="formState" /> <LazySmartsheetColumnRollupOptions v-if="formState.uidt === UITypes.Rollup" v-model:value="formState" />
<LazySmartsheetColumnLinkedToAnotherRecordOptions <LazySmartsheetColumnLinkedToAnotherRecordOptions
v-if="!isEdit && (formState.uidt === UITypes.LinkToAnotherRecord || formState.uidt === UITypes.Links)" v-if="!isEdit && (formState.uidt === UITypes.LinkToAnotherRecord || formState.uidt === UITypes.Links)"
:key="formState.uidt"
v-model:value="formState" v-model:value="formState"
/> />
<LazySmartsheetColumnLinkOptions v-if="isEdit && formState.uidt === UITypes.Links" v-model:value="formState" /> <LazySmartsheetColumnLinkOptions v-if="isEdit && formState.uidt === UITypes.Links" v-model:value="formState" />

4
packages/nc-gui/components/smartsheet/column/SelectOptions.vue

@ -113,7 +113,7 @@ onMounted(() => {
op.title = op.title.replace(/^'/, '').replace(/'$/, '') op.title = op.title.replace(/^'/, '').replace(/'$/, '')
} }
if (vModel.value.cdf) { if (vModel.value.cdf && typeof vModel.value.cdf === 'string') {
const fndDefaultOption = options.value.find((el) => el.title === vModel.value.cdf) const fndDefaultOption = options.value.find((el) => el.title === vModel.value.cdf)
if (!fndDefaultOption) { if (!fndDefaultOption) {
vModel.value.cdf = vModel.value.cdf.replace(/^'/, '').replace(/'$/, '') vModel.value.cdf = vModel.value.cdf.replace(/^'/, '').replace(/'$/, '')
@ -249,7 +249,7 @@ const undoRemoveRenderedOption = (index: number) => {
// Removes the Select Option from cdf if the option is removed // Removes the Select Option from cdf if the option is removed
watch(vModel.value, (next) => { watch(vModel.value, (next) => {
const cdfs = (next.cdf ?? '').split(',') const cdfs = (next.cdf ?? '').toString().split(',')
const values = (next.colOptions.options ?? []).map((col) => { const values = (next.colOptions.options ?? []).map((col) => {
return col.title.replace(/^'/, '').replace(/'$/, '') return col.title.replace(/^'/, '').replace(/'$/, '')
}) })

8
packages/nc-gui/components/virtual-cell/Lookup.vue

@ -110,9 +110,13 @@ const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning, activ
> >
<template v-if="lookupColumn"> <template v-if="lookupColumn">
<!-- Render virtual cell --> <!-- Render virtual cell -->
<div v-if="isVirtualCol(lookupColumn)"> <div v-if="isVirtualCol(lookupColumn)" class="flex">
<!-- If non-belongs-to LTAR column then pass the array value, else iterate and render -->
<template <template
v-if="lookupColumn.uidt === UITypes.LinkToAnotherRecord && lookupColumn.colOptions.type === RelationTypes.BELONGS_TO" v-if="
lookupColumn.uidt !== UITypes.LinkToAnotherRecord ||
(lookupColumn.uidt === UITypes.LinkToAnotherRecord && lookupColumn.colOptions.type === RelationTypes.BELONGS_TO)
"
> >
<LazySmartsheetVirtualCell <LazySmartsheetVirtualCell
v-for="(v, i) of arrValue" v-for="(v, i) of arrValue"

3
packages/nc-gui/components/virtual-cell/QrCode.vue

@ -92,9 +92,10 @@ const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning } = us
:style="{ height: rowHeight ? `${rowHeight * 1.4}rem` : `1.4rem` }" :style="{ height: rowHeight ? `${rowHeight * 1.4}rem` : `1.4rem` }"
:src="qrCode" :src="qrCode"
:alt="$t('title.qrCode')" :alt="$t('title.qrCode')"
class="min-w-[1.4em]"
@click="showQrModal" @click="showQrModal"
/> />
<img v-else-if="showQrCode" class="mx-auto" :src="qrCode" :alt="$t('title.qrCode')" @click="showQrModal" /> <img v-else-if="showQrCode" class="mx-auto min-w-[1.4em]" :src="qrCode" :alt="$t('title.qrCode')" @click="showQrModal" />
</div> </div>
<div v-if="showEditNonEditableFieldWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs"> <div v-if="showEditNonEditableFieldWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs">
{{ $t('msg.warning.nonEditableFields.computedFieldUnableToClear') }} {{ $t('msg.warning.nonEditableFields.computedFieldUnableToClear') }}

2
packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts

@ -987,7 +987,7 @@ export class MysqlUi {
colProp.dt = 'set'; colProp.dt = 'set';
if ( if (
col.colOptions?.options.length > 64 || col.colOptions?.options.length > 64 ||
col.dtxp?.split(',').length > 64 col.dtxp?.toString?.()?.split(',').length > 64
) { ) {
colProp.dt = 'text'; colProp.dt = 'text';
} }

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

@ -2277,7 +2277,6 @@ class BaseModelSqlv2 {
async insert(data, trx?, cookie?, _disableOptimization = false) { async insert(data, trx?, cookie?, _disableOptimization = false) {
try { try {
const columns = await this.model.getColumns(); const columns = await this.model.getColumns();
// exclude auto increment columns in body // exclude auto increment columns in body
@ -2289,7 +2288,6 @@ class BaseModelSqlv2 {
if (data[keyName]) { if (data[keyName]) {
delete data[keyName]; delete data[keyName];
} }
} }
} }

22
packages/nocodb/src/db/generateLookupSelectQuery.ts

@ -28,6 +28,11 @@ export async function getDisplayValueOfRefTable(
.then((cols) => cols.find((col) => col.pv)); .then((cols) => cols.find((col) => col.pv));
} }
// this function will generate the query for lookup column
// or for LTAR column and return the query builder
// query result will be aggregated json array string in case of Myssql and Postgres
// and string with separator in case of sqlite and mysql
// this function is used for sorting and grouping of lookup/LTAR column at the moment
export default async function generateLookupSelectQuery({ export default async function generateLookupSelectQuery({
column, column,
baseModelSqlv2, baseModelSqlv2,
@ -392,8 +397,23 @@ export default async function generateLookupSelectQuery({
) )
.from(selectQb.as(subQueryAlias)), .from(selectQb.as(subQueryAlias)),
}; };
} else if (baseModelSqlv2.isMssql) {
// ref: https://stackoverflow.com/questions/13382856/sqlite3-join-group-concat-using-distinct-with-custom-separator
// selectQb.orderBy(`${lookupColumn.title}`, 'asc');
return {
builder: knex
.select(
knex.raw(`STRING_AGG(??, ?)`, [
lookupColumn.title,
LOOKUP_VAL_SEPARATOR,
]),
)
.from(selectQb.as(subQueryAlias)),
};
} }
NcError.notImplemented('Database not supported Group by on Lookup'); NcError.notImplemented(
'Database not supported this operation on Lookup/LTAR',
);
} }
} }

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

@ -12,6 +12,7 @@ import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2';
import genRollupSelectv2 from '~/db/genRollupSelectv2'; import genRollupSelectv2 from '~/db/genRollupSelectv2';
import { sanitize } from '~/helpers/sqlSanitize'; import { sanitize } from '~/helpers/sqlSanitize';
import { Sort } from '~/models'; import { Sort } from '~/models';
import generateLookupSelectQuery from '~/db/generateLookupSelectQuery';
export default async function sortV2( export default async function sortV2(
baseModelSqlv2: BaseModelSqlv2, baseModelSqlv2: BaseModelSqlv2,
@ -77,176 +78,21 @@ export default async function sortV2(
} }
break; break;
case UITypes.Lookup: case UITypes.Lookup:
case UITypes.LinkToAnotherRecord:
{ {
const rootAlias = alias; const rootAlias = alias;
{ {
let aliasCount = 0, const selectQb = await generateLookupSelectQuery({
selectQb; baseModelSqlv2,
const alias = `__nc_sort${aliasCount++}`; column,
const lookup = await column.getColOptions<LookupColumn>(); alias: rootAlias,
{ model,
const relationCol = await lookup.getRelationColumn(); });
const relation =
await relationCol.getColOptions<LinkToAnotherRecordColumn>();
if (relation.type !== RelationTypes.BELONGS_TO) return;
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
selectQb = knex(
`${baseModelSqlv2.getTnPath(
parentModel.table_name,
)} as ${alias}`,
).where(
`${alias}.${parentColumn.column_name}`,
knex.raw(`??`, [
`${
rootAlias || baseModelSqlv2.getTnPath(childModel.table_name)
}.${childColumn.column_name}`,
]),
);
}
let lookupColumn = await lookup.getLookupColumn();
let prevAlias = alias;
while (lookupColumn.uidt === UITypes.Lookup) {
const nestedAlias = `__nc_sort${aliasCount++}`;
const nestedLookup =
await lookupColumn.getColOptions<LookupColumn>();
const relationCol = await nestedLookup.getRelationColumn();
const relation =
await relationCol.getColOptions<LinkToAnotherRecordColumn>();
// if any of the relation in nested lookup is
// not belongs to then ignore the sort option
if (relation.type !== 'bt') return;
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
selectQb.join(
`${baseModelSqlv2.getTnPath(
parentModel.table_name,
)} as ${nestedAlias}`,
`${nestedAlias}.${parentColumn.column_name}`,
`${prevAlias}.${childColumn.column_name}`,
);
lookupColumn = await nestedLookup.getLookupColumn();
prevAlias = nestedAlias;
}
switch (lookupColumn.uidt) {
case UITypes.Links:
case UITypes.Rollup:
{
const builder = (
await genRollupSelectv2({
baseModelSqlv2,
knex,
columnOptions:
(await lookupColumn.getColOptions()) as RollupColumn,
alias: prevAlias,
})
).builder;
selectQb.select(builder);
}
break;
case UITypes.LinkToAnotherRecord:
{
const nestedAlias = `__nc_sort${aliasCount++}`;
const relation =
await lookupColumn.getColOptions<LinkToAnotherRecordColumn>();
if (relation.type !== 'bt') return;
const colOptions =
(await column.getColOptions()) as LinkToAnotherRecordColumn;
const childColumn = await colOptions.getChildColumn();
const parentColumn = await colOptions.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
selectQb
.join(
`${baseModelSqlv2.getTnPath(
parentModel.table_name,
)} as ${nestedAlias}`,
`${nestedAlias}.${parentColumn.column_name}`,
`${prevAlias}.${childColumn.column_name}`,
)
.select(parentModel?.displayValue?.column_name);
}
break;
case UITypes.Formula:
{
const builder = (
await formulaQueryBuilderv2(
baseModelSqlv2,
(
await column.getColOptions<FormulaColumn>()
).formula,
null,
model,
column,
)
).builder;
selectQb.select(builder);
}
break;
default:
{
selectQb.select(`${prevAlias}.${lookupColumn.column_name}`);
}
break;
}
qb.orderBy(selectQb, sort.direction || 'asc', nulls); qb.orderBy(selectQb?.builder, sort.direction || 'asc', nulls);
} }
} }
break; break;
case UITypes.LinkToAnotherRecord:
{
const relation =
await column.getColOptions<LinkToAnotherRecordColumn>();
if (relation.type !== 'bt') return;
const colOptions =
(await column.getColOptions()) as LinkToAnotherRecordColumn;
const childColumn = await colOptions.getChildColumn();
const parentColumn = await colOptions.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const selectQb = knex(
baseModelSqlv2.getTnPath(parentModel.table_name),
)
.select(parentModel?.displayValue?.column_name)
.where(
`${baseModelSqlv2.getTnPath(parentModel.table_name)}.${
parentColumn.column_name
}`,
knex.raw(`??`, [
`${baseModelSqlv2.getTnPath(childModel.table_name)}.${
childColumn.column_name
}`,
]),
);
qb.orderBy(selectQb, sort.direction || 'asc', nulls);
}
break;
case UITypes.SingleSelect: { case UITypes.SingleSelect: {
const clientType = knex.clientType(); const clientType = knex.clientType();
if (clientType === 'mysql' || clientType === 'mysql2') { if (clientType === 'mysql' || clientType === 'mysql2') {

Loading…
Cancel
Save