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. 20
      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

20
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" class="flex-shrink-1 flex-grow-0 caption nc-filter-operation-select"
:items="filterComparisonOp(filter)" :items="filterComparisonOp(filter)"
:placeholder="$t('labels.operation')" :placeholder="$t('labels.operation')"
v-show="filter && filter.fk_column_id"
solo solo
flat flat
style="max-width: 120px" style="max-width: 120px"
@ -142,6 +143,7 @@
v-else v-else
:key="i + '_7'" :key="i + '_7'"
v-model="filter.value" v-model="filter.value"
v-show="filter && filter.fk_column_id"
solo solo
flat flat
hide-details hide-details
@ -324,12 +326,22 @@ export default {
if ( if (
f && f &&
f.fk_column_id && f.fk_column_id &&
this.columnsById[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
) { ) {
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) 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 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)) { if (Array.isArray(_filter)) {
const qbs = await Promise.all( const qbs = await Promise.all(
_filter.map(child => parseConditionV2(child, knex, aliasCount)) _filter.map((child) => parseConditionV2(child, knex, aliasCount))
); );
return qbP => { return (qbP) => {
qbP.where(qb => { qbP.where((qb) => {
for (const [i, qb1] of Object.entries(qbs)) { for (const [i, qb1] of Object.entries(qbs)) {
qb[getLogicalOpMethod(_filter[i])](qb1); qb[getLogicalOpMethod(_filter[i])](qb1);
} }
@ -64,11 +64,11 @@ const parseConditionV2 = async (
const children = await filter.getChildren(); const children = await filter.getChildren();
const qbs = await Promise.all( const qbs = await Promise.all(
(children || []).map(child => parseConditionV2(child, knex, aliasCount)) (children || []).map((child) => parseConditionV2(child, knex, aliasCount))
); );
return qbP => { return (qbP) => {
qbP[getLogicalOpMethod(filter)](qb => { qbP[getLogicalOpMethod(filter)]((qb) => {
for (const [i, qb1] of Object.entries(qbs)) { for (const [i, qb1] of Object.entries(qbs)) {
qb[getLogicalOpMethod(children[i])](qb1); qb[getLogicalOpMethod(children[i])](qb1);
} }
@ -78,7 +78,8 @@ const parseConditionV2 = async (
const column = await filter.getColumn(); const column = await filter.getColumn();
if (!column) return () => {}; if (!column) return () => {};
if (column.uidt === UITypes.LinkToAnotherRecord) { 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 childColumn = await colOptions.getChildColumn();
const parentColumn = await colOptions.getParentColumn(); const parentColumn = await colOptions.getParentColumn();
const childModel = await childColumn.getModel(); const childModel = await childColumn.getModel();
@ -86,6 +87,28 @@ const parseConditionV2 = async (
const parentModel = await parentColumn.getModel(); const parentModel = await parentColumn.getModel();
await parentModel.getColumns(); await parentModel.getColumns();
if (colOptions.type === RelationTypes.HAS_MANY) { 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( const selectQb = knex(childModel.table_name).select(
childColumn.column_name childColumn.column_name
); );
@ -97,7 +120,7 @@ const parseConditionV2 = async (
? negatedMapping[filter.comparison_op] ? negatedMapping[filter.comparison_op]
: {}), : {}),
fk_model_id: childModel.id, fk_model_id: childModel.id,
fk_column_id: childModel?.primaryValue?.id fk_column_id: childModel?.primaryValue?.id,
}), }),
knex, knex,
aliasCount aliasCount
@ -110,6 +133,17 @@ const parseConditionV2 = async (
else qbP.whereIn(parentColumn.column_name, selectQb); else qbP.whereIn(parentColumn.column_name, selectQb);
}; };
} else if (colOptions.type === RelationTypes.BELONGS_TO) { } 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( const selectQb = knex(parentModel.table_name).select(
parentColumn.column_name parentColumn.column_name
); );
@ -121,7 +155,7 @@ const parseConditionV2 = async (
? negatedMapping[filter.comparison_op] ? negatedMapping[filter.comparison_op]
: {}), : {}),
fk_model_id: parentModel.id, fk_model_id: parentModel.id,
fk_column_id: parentModel?.primaryValue?.id fk_column_id: parentModel?.primaryValue?.id,
}), }),
knex, knex,
aliasCount aliasCount
@ -138,6 +172,29 @@ const parseConditionV2 = async (
const mmParentColumn = await colOptions.getMMParentColumn(); const mmParentColumn = await colOptions.getMMParentColumn();
const mmChildColumn = await colOptions.getMMChildColumn(); 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) const selectQb = knex(mmModel.table_name)
.select(mmChildColumn.column_name) .select(mmChildColumn.column_name)
.join( .join(
@ -153,7 +210,7 @@ const parseConditionV2 = async (
? negatedMapping[filter.comparison_op] ? negatedMapping[filter.comparison_op]
: {}), : {}),
fk_model_id: parentModel.id, fk_model_id: parentModel.id,
fk_column_id: parentModel?.primaryValue?.id fk_column_id: parentModel?.primaryValue?.id,
}), }),
knex, knex,
aliasCount aliasCount
@ -167,7 +224,7 @@ const parseConditionV2 = async (
}; };
} }
return _qb => {}; return (_qb) => {};
} else if (column.uidt === UITypes.Lookup) { } else if (column.uidt === UITypes.Lookup) {
return await generateLookupCondition(column, filter, knex, aliasCount); return await generateLookupCondition(column, filter, knex, aliasCount);
} else if (column.uidt === UITypes.Rollup && !customWhereClause) { } else if (column.uidt === UITypes.Rollup && !customWhereClause) {
@ -175,7 +232,7 @@ const parseConditionV2 = async (
await genRollupSelectv2({ await genRollupSelectv2({
knex, knex,
alias, alias,
columnOptions: (await column.getColOptions()) as RollupColumn columnOptions: (await column.getColOptions()) as RollupColumn,
}) })
).builder; ).builder;
return parseConditionV2( return parseConditionV2(
@ -213,7 +270,7 @@ const parseConditionV2 = async (
); );
let val = customWhereClause ? customWhereClause : filter.value; let val = customWhereClause ? customWhereClause : filter.value;
return qb => { return (qb) => {
switch (filter.comparison_op) { switch (filter.comparison_op) {
case 'eq': case 'eq':
qb = qb.where(field, val); qb = qb.where(field, val);
@ -321,7 +378,7 @@ const parseConditionV2 = async (
const negatedMapping = { const negatedMapping = {
nlike: { comparison_op: 'like' }, nlike: { comparison_op: 'like' },
neq: { comparison_op: 'eq' } neq: { comparison_op: 'eq' },
}; };
function getAlias(aliasCount: { count: number }) { function getAlias(aliasCount: { count: number }) {
@ -337,9 +394,8 @@ async function generateLookupCondition(
): Promise<any> { ): Promise<any> {
const colOptions = await col.getColOptions<LookupColumn>(); const colOptions = await col.getColOptions<LookupColumn>();
const relationColumn = await colOptions.getRelationColumn(); const relationColumn = await colOptions.getRelationColumn();
const relationColumnOptions = await relationColumn.getColOptions< const relationColumnOptions =
LinkToAnotherRecordColumn await relationColumn.getColOptions<LinkToAnotherRecordColumn>();
>();
// const relationModel = await relationColumn.getModel(); // const relationModel = await relationColumn.getModel();
const lookupColumn = await colOptions.getLookupColumn(); const lookupColumn = await colOptions.getLookupColumn();
const alias = getAlias(aliasCount); const alias = getAlias(aliasCount);
@ -362,7 +418,7 @@ async function generateLookupCondition(
...filter, ...filter,
...(filter.comparison_op in negatedMapping ...(filter.comparison_op in negatedMapping
? negatedMapping[filter.comparison_op] ? negatedMapping[filter.comparison_op]
: {}) : {}),
}, },
lookupColumn, lookupColumn,
qb, qb,
@ -385,7 +441,7 @@ async function generateLookupCondition(
...filter, ...filter,
...(filter.comparison_op in negatedMapping ...(filter.comparison_op in negatedMapping
? negatedMapping[filter.comparison_op] ? negatedMapping[filter.comparison_op]
: {}) : {}),
}, },
lookupColumn, lookupColumn,
qb, qb,
@ -419,7 +475,7 @@ async function generateLookupCondition(
...filter, ...filter,
...(filter.comparison_op in negatedMapping ...(filter.comparison_op in negatedMapping
? negatedMapping[filter.comparison_op] ? negatedMapping[filter.comparison_op]
: {}) : {}),
}, },
lookupColumn, lookupColumn,
qb, qb,
@ -455,9 +511,8 @@ async function nestedConditionJoin(
await lookupColumn.getColOptions<LookupColumn>() await lookupColumn.getColOptions<LookupColumn>()
).getRelationColumn() ).getRelationColumn()
: lookupColumn; : lookupColumn;
const relationColOptions = await relationColumn.getColOptions< const relationColOptions =
LinkToAnotherRecordColumn await relationColumn.getColOptions<LinkToAnotherRecordColumn>();
>();
const relAlias = `__nc${aliasCount.count++}`; const relAlias = `__nc${aliasCount.count++}`;
const childColumn = await relationColOptions.getChildColumn(); const childColumn = await relationColOptions.getChildColumn();
@ -528,7 +583,7 @@ async function nestedConditionJoin(
new Filter({ new Filter({
...filter, ...filter,
fk_model_id: childModel.id, fk_model_id: childModel.id,
fk_column_id: childModel.primaryValue?.id fk_column_id: childModel.primaryValue?.id,
}), }),
knex, knex,
aliasCount, aliasCount,
@ -544,7 +599,7 @@ async function nestedConditionJoin(
new Filter({ new Filter({
...filter, ...filter,
fk_model_id: parentModel.id, fk_model_id: parentModel.id,
fk_column_id: parentModel?.primaryValue?.id fk_column_id: parentModel?.primaryValue?.id,
}), }),
knex, knex,
aliasCount, aliasCount,
@ -560,7 +615,7 @@ async function nestedConditionJoin(
new Filter({ new Filter({
...filter, ...filter,
fk_model_id: parentModel.id, fk_model_id: parentModel.id,
fk_column_id: parentModel.primaryValue?.id fk_column_id: parentModel.primaryValue?.id,
}), }),
knex, knex,
aliasCount, aliasCount,
@ -577,7 +632,7 @@ async function nestedConditionJoin(
new Filter({ new Filter({
...filter, ...filter,
fk_model_id: (await lookupColumn.getModel()).id, fk_model_id: (await lookupColumn.getModel()).id,
fk_column_id: lookupColumn?.id fk_column_id: lookupColumn?.id,
}), }),
knex, knex,
aliasCount, aliasCount,

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

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

Loading…
Cancel
Save