Browse Source

Merge pull request #2535 from nocodb/fix/filter

fix: filters / conditions
pull/2568/head
աɨռɢӄաօռɢ 2 years ago committed by GitHub
parent
commit
80a350aee3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      packages/nc-gui/components/project/spreadsheet/components/ColumnFilter.vue
  2. 109
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts
  3. 2
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/helpers/sanitize.ts

24
packages/nc-gui/components/project/spreadsheet/components/ColumnFilter.vue

@ -108,6 +108,7 @@
class="flex-shrink-1 flex-grow-0 caption nc-filter-operation-select"
:items="filterComparisonOp(filter)"
:placeholder="$t('labels.operation')"
v-show="filter && filter.fk_column_id"
solo
flat
style="max-width: 120px"
@ -142,6 +143,7 @@
v-else
:key="i + '_7'"
v-model="filter.value"
v-show="filter && filter.fk_column_id"
solo
flat
hide-details
@ -324,13 +326,23 @@ export default {
if (
f &&
f.fk_column_id &&
this.columnsById[f.fk_column_id] &&
this.columnsById[f.fk_column_id].uidt ===
UITypes.LinkToAnotherRecord &&
this.columnsById[f.fk_column_id].uidt === UITypes.Lookup
this.columnsById[f.fk_column_id]
) {
return !['notempty', 'empty', 'notnull', 'null'].includes(op.value)
}
const uidt = this.columnsById[f.fk_column_id].uidt
if (uidt === UITypes.Lookup) {
// TODO: handle it later
return !['notempty', 'empty', 'notnull', 'null'].includes(op.value)
} else if (uidt === UITypes.LinkToAnotherRecord) {
const type = this.columnsById[f.fk_column_id].colOptions.type
if (type === 'hm' || type === 'mm') {
// exclude notnull & null
return !['notnull', 'null'].includes(op.value)
} else if (type === 'bt') {
// exclude notempty & empty
return !['notempty', 'empty'].includes(op.value)
}
}
}
return true
})
},

109
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts

@ -50,11 +50,11 @@ const parseConditionV2 = async (
}
if (Array.isArray(_filter)) {
const qbs = await Promise.all(
_filter.map(child => parseConditionV2(child, knex, aliasCount))
_filter.map((child) => parseConditionV2(child, knex, aliasCount))
);
return qbP => {
qbP.where(qb => {
return (qbP) => {
qbP.where((qb) => {
for (const [i, qb1] of Object.entries(qbs)) {
qb[getLogicalOpMethod(_filter[i])](qb1);
}
@ -64,11 +64,11 @@ const parseConditionV2 = async (
const children = await filter.getChildren();
const qbs = await Promise.all(
(children || []).map(child => parseConditionV2(child, knex, aliasCount))
(children || []).map((child) => parseConditionV2(child, knex, aliasCount))
);
return qbP => {
qbP[getLogicalOpMethod(filter)](qb => {
return (qbP) => {
qbP[getLogicalOpMethod(filter)]((qb) => {
for (const [i, qb1] of Object.entries(qbs)) {
qb[getLogicalOpMethod(children[i])](qb1);
}
@ -78,7 +78,8 @@ const parseConditionV2 = async (
const column = await filter.getColumn();
if (!column) return () => {};
if (column.uidt === UITypes.LinkToAnotherRecord) {
const colOptions = (await column.getColOptions()) as LinkToAnotherRecordColumn;
const colOptions =
(await column.getColOptions()) as LinkToAnotherRecordColumn;
const childColumn = await colOptions.getChildColumn();
const parentColumn = await colOptions.getParentColumn();
const childModel = await childColumn.getModel();
@ -86,6 +87,28 @@ const parseConditionV2 = async (
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
if (colOptions.type === RelationTypes.HAS_MANY) {
if (
filter.comparison_op === 'empty' ||
filter.comparison_op === 'notempty'
) {
const selectHmCount = knex(childModel.table_name)
.count(childColumn.column_name)
.where(
childColumn.column_name,
knex.raw('??.??', [
alias || parentModel.table_name,
parentColumn.column_name,
])
);
return (qb) => {
if (filter.comparison_op === 'empty') {
qb.where(knex.raw('0'), selectHmCount);
} else {
qb.whereNot(knex.raw('0'), selectHmCount);
}
};
}
const selectQb = knex(childModel.table_name).select(
childColumn.column_name
);
@ -97,7 +120,7 @@ const parseConditionV2 = async (
? negatedMapping[filter.comparison_op]
: {}),
fk_model_id: childModel.id,
fk_column_id: childModel?.primaryValue?.id
fk_column_id: childModel?.primaryValue?.id,
}),
knex,
aliasCount
@ -110,6 +133,17 @@ const parseConditionV2 = async (
else qbP.whereIn(parentColumn.column_name, selectQb);
};
} else if (colOptions.type === RelationTypes.BELONGS_TO) {
if (filter.comparison_op === 'null') {
return (qb) => {
qb.whereNull(childColumn.column_name);
};
}
if (filter.comparison_op === 'notnull') {
return (qb) => {
qb.whereNotNull(childColumn.column_name);
};
}
const selectQb = knex(parentModel.table_name).select(
parentColumn.column_name
);
@ -121,7 +155,7 @@ const parseConditionV2 = async (
? negatedMapping[filter.comparison_op]
: {}),
fk_model_id: parentModel.id,
fk_column_id: parentModel?.primaryValue?.id
fk_column_id: parentModel?.primaryValue?.id,
}),
knex,
aliasCount
@ -138,6 +172,29 @@ const parseConditionV2 = async (
const mmParentColumn = await colOptions.getMMParentColumn();
const mmChildColumn = await colOptions.getMMChildColumn();
if (
filter.comparison_op === 'empty' ||
filter.comparison_op === 'notempty'
) {
const selectMmCount = knex(mmModel.table_name)
.count(mmChildColumn.column_name)
.where(
mmChildColumn.column_name,
knex.raw('??.??', [
alias || childModel.table_name,
childColumn.column_name,
])
);
return (qb) => {
if (filter.comparison_op === 'empty') {
qb.where(knex.raw('0'), selectMmCount);
} else {
qb.whereNot(knex.raw('0'), selectMmCount);
}
};
}
const selectQb = knex(mmModel.table_name)
.select(mmChildColumn.column_name)
.join(
@ -153,7 +210,7 @@ const parseConditionV2 = async (
? negatedMapping[filter.comparison_op]
: {}),
fk_model_id: parentModel.id,
fk_column_id: parentModel?.primaryValue?.id
fk_column_id: parentModel?.primaryValue?.id,
}),
knex,
aliasCount
@ -167,7 +224,7 @@ const parseConditionV2 = async (
};
}
return _qb => {};
return (_qb) => {};
} else if (column.uidt === UITypes.Lookup) {
return await generateLookupCondition(column, filter, knex, aliasCount);
} else if (column.uidt === UITypes.Rollup && !customWhereClause) {
@ -175,7 +232,7 @@ const parseConditionV2 = async (
await genRollupSelectv2({
knex,
alias,
columnOptions: (await column.getColOptions()) as RollupColumn
columnOptions: (await column.getColOptions()) as RollupColumn,
})
).builder;
return parseConditionV2(
@ -213,7 +270,7 @@ const parseConditionV2 = async (
);
let val = customWhereClause ? customWhereClause : filter.value;
return qb => {
return (qb) => {
switch (filter.comparison_op) {
case 'eq':
qb = qb.where(field, val);
@ -321,7 +378,7 @@ const parseConditionV2 = async (
const negatedMapping = {
nlike: { comparison_op: 'like' },
neq: { comparison_op: 'eq' }
neq: { comparison_op: 'eq' },
};
function getAlias(aliasCount: { count: number }) {
@ -337,9 +394,8 @@ async function generateLookupCondition(
): Promise<any> {
const colOptions = await col.getColOptions<LookupColumn>();
const relationColumn = await colOptions.getRelationColumn();
const relationColumnOptions = await relationColumn.getColOptions<
LinkToAnotherRecordColumn
>();
const relationColumnOptions =
await relationColumn.getColOptions<LinkToAnotherRecordColumn>();
// const relationModel = await relationColumn.getModel();
const lookupColumn = await colOptions.getLookupColumn();
const alias = getAlias(aliasCount);
@ -362,7 +418,7 @@ async function generateLookupCondition(
...filter,
...(filter.comparison_op in negatedMapping
? negatedMapping[filter.comparison_op]
: {})
: {}),
},
lookupColumn,
qb,
@ -385,7 +441,7 @@ async function generateLookupCondition(
...filter,
...(filter.comparison_op in negatedMapping
? negatedMapping[filter.comparison_op]
: {})
: {}),
},
lookupColumn,
qb,
@ -419,7 +475,7 @@ async function generateLookupCondition(
...filter,
...(filter.comparison_op in negatedMapping
? negatedMapping[filter.comparison_op]
: {})
: {}),
},
lookupColumn,
qb,
@ -455,9 +511,8 @@ async function nestedConditionJoin(
await lookupColumn.getColOptions<LookupColumn>()
).getRelationColumn()
: lookupColumn;
const relationColOptions = await relationColumn.getColOptions<
LinkToAnotherRecordColumn
>();
const relationColOptions =
await relationColumn.getColOptions<LinkToAnotherRecordColumn>();
const relAlias = `__nc${aliasCount.count++}`;
const childColumn = await relationColOptions.getChildColumn();
@ -528,7 +583,7 @@ async function nestedConditionJoin(
new Filter({
...filter,
fk_model_id: childModel.id,
fk_column_id: childModel.primaryValue?.id
fk_column_id: childModel.primaryValue?.id,
}),
knex,
aliasCount,
@ -544,7 +599,7 @@ async function nestedConditionJoin(
new Filter({
...filter,
fk_model_id: parentModel.id,
fk_column_id: parentModel?.primaryValue?.id
fk_column_id: parentModel?.primaryValue?.id,
}),
knex,
aliasCount,
@ -560,7 +615,7 @@ async function nestedConditionJoin(
new Filter({
...filter,
fk_model_id: parentModel.id,
fk_column_id: parentModel.primaryValue?.id
fk_column_id: parentModel.primaryValue?.id,
}),
knex,
aliasCount,
@ -577,7 +632,7 @@ async function nestedConditionJoin(
new Filter({
...filter,
fk_model_id: (await lookupColumn.getModel()).id,
fk_column_id: lookupColumn?.id
fk_column_id: lookupColumn?.id,
}),
knex,
aliasCount,

2
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/helpers/sanitize.ts

@ -1,9 +1,11 @@
export function sanitize(v) {
if (typeof v !== 'string') return v;
return v?.replace(/([^\\]|^)(\?+)/g, (_, m1, m2) => {
return `${m1}${m2.split('?').join('\\?')}`;
});
}
export function unsanitize(v) {
if (typeof v !== 'string') return v;
return v?.replace(/\\[?]/g, '?');
}

Loading…
Cancel
Save