Browse Source

Merge pull request #4379 from southball/feature/multiselect-filter

feat: add filter types for multiselect field
pull/4891/head
Pranav C 2 years ago committed by GitHub
parent
commit
b09d16ceb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
  2. 20
      packages/nc-gui/utils/filterUtils.ts
  3. 4
      packages/noco-docs/content/en/developer-resources/rest-apis.md
  4. 42
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/conditionV2.ts
  5. 20
      packages/nocodb/src/lib/meta/helpers/webhookHelpers.ts
  6. 4
      packages/nocodb/src/lib/models/Filter.ts

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

@ -1,5 +1,5 @@
<script setup lang="ts">
import type { FilterType } from 'nocodb-sdk'
import type { ColumnType, FilterType } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk'
import {
ActiveViewInj,
@ -100,6 +100,11 @@ watch(
},
)
const getApplicableFilters = (id?: string) => {
const colType = (meta.value?.columnsById as Record<string, ColumnType>)?.[id ?? '']?.uidt
return comparisonOpList.filter((op) => !op.types || op.types.includes(colType))
}
const applyChanges = async (hookId?: string, _nested = false) => {
await sync(hookId, _nested)
@ -213,7 +218,12 @@ defineExpose({
dropdown-class-name="nc-dropdown-filter-comp-op"
@change="filterUpdateCondition(filter, i)"
>
<a-select-option v-for="compOp in comparisonOpList" :key="compOp.value" :value="compOp.value" class="">
<a-select-option
v-for="compOp in getApplicableFilters(filter.fk_column_id)"
:key="compOp.value"
:value="compOp.value"
class=""
>
{{ compOp.text }}
</a-select-option>
</a-select>

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

@ -35,6 +35,26 @@ export const comparisonOpList = [
value: 'notnull',
ignoreVal: true,
},
{
text: 'contains all of',
value: 'allof',
types: ['MultiSelect'],
},
{
text: 'contains any of',
value: 'anyof',
types: ['MultiSelect'],
},
{
text: 'does not contain all of',
value: 'nallof',
types: ['MultiSelect'],
},
{
text: 'does not contain any of',
value: 'nanyof',
types: ['MultiSelect'],
},
{
text: '>',
value: 'gt',

4
packages/noco-docs/content/en/developer-resources/rest-apis.md

@ -221,6 +221,10 @@ Currently, the default value for {orgs} is <b>noco</b>. Users will be able to ch
| btw | between | (colName,btw,val1,val2) |
| nbtw | not between | (colName,nbtw,val1,val2) |
| like | like | (colName,like,%name) |
| allof | includes all of | (colName,allof,val1,val2,...) |
| anyof | includes any of | (colName,anyof,val1,val2,...) |
| nallof | does not include all of (includes none or some, but not all of) | (colName,nallof,val1,val2,...) |
| nanyof | does not include any of (includes none of) | (colName,nanyof,val1,val2,...) |
## Logical Operators

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

@ -270,7 +270,7 @@ const parseConditionV2 = async (
);
const _val = customWhereClause ? customWhereClause : filter.value;
return (qb) => {
return (qb: Knex.QueryBuilder) => {
let [field, val] = [_field, _val];
switch (filter.comparison_op) {
case 'eq':
@ -306,6 +306,46 @@ const parseConditionV2 = async (
val
);
break;
case 'allof':
case 'anyof':
case 'nallof':
case 'nanyof':
// Condition for filter, without negation
const condition = (builder: Knex.QueryBuilder) => {
const items = val.split(',').map((item) => item.trim());
for (let i = 0; i < items.length; i++) {
let sql;
const bindings = [field, `%,${items[i]},%`];
if (qb?.client?.config?.client === 'pg') {
sql = "(',' || ??::text || ',') ilike ?";
} else if (qb?.client?.config?.client === 'sqlite3') {
sql = "(',' || ?? || ',') like ?";
} else {
sql = "CONCAT(',', ??, ',') like ?";
}
if (i === 0) {
builder = builder.whereRaw(sql, bindings);
} else {
if (
filter.comparison_op === 'allof' ||
filter.comparison_op === 'nallof'
) {
builder = builder.andWhereRaw(sql, bindings);
} else {
builder = builder.orWhereRaw(sql, bindings);
}
}
}
};
if (
filter.comparison_op === 'allof' ||
filter.comparison_op === 'anyof'
) {
qb = qb.where(condition);
} else {
qb = qb.whereNot(condition).orWhereNull(field);
}
break;
case 'gt':
qb = qb.where(field, customWhereClause ? '<' : '>', val);
break;

20
packages/nocodb/src/lib/meta/helpers/webhookHelpers.ts

@ -72,6 +72,26 @@ export async function validateCondition(filters: Filter[], data: any) {
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;

4
packages/nocodb/src/lib/models/Filter.ts

@ -32,6 +32,10 @@ export default class Filter {
| 'notempty'
| 'null'
| 'notnull'
| 'allof'
| 'anyof'
| 'nallof'
| 'nanyof'
| 'gt'
| 'lt'
| 'gte'

Loading…
Cancel
Save