feat: string operators (#215)

* feat: string operators

* fix: remove filter

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
ChengLei Shao 2022-03-02 15:13:06 +08:00 committed by GitHub
parent a96f76b87a
commit aa1c84e72a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 98 additions and 56 deletions

View File

@ -11,7 +11,6 @@ import { i18n } from '../../../i18n';
import { ActionBar, ActionContext } from '../action'; import { ActionBar, ActionContext } from '../action';
import { CalendarContext, ToolbarContext } from './context'; import { CalendarContext, ToolbarContext } from './context';
import { Event } from './Event'; import { Event } from './Event';
import { Filter } from './Filter';
import { Nav } from './Nav'; import { Nav } from './Nav';
import './style.less'; import './style.less';
import { Title } from './Title'; import { Title } from './Title';
@ -182,5 +181,3 @@ Calendar.Today = Today;
Calendar.Nav = Nav; Calendar.Nav = Nav;
Calendar.ViewSelect = ViewSelect; Calendar.ViewSelect = ViewSelect;
Calendar.Filter = Filter;

View File

@ -1,41 +0,0 @@
import { createForm } from '@formily/core';
import { observer, useFieldSchema } from '@formily/react';
import flatten from 'flat';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaComponent } from '../../core';
import { useDesignable } from '../../hooks';
import { useCompile } from '../../hooks/useCompile';
import { FilterContext } from '../filter/context';
export const Filter = observer((props: any) => {
const { t } = useTranslation();
const compile = useCompile();
const { properties } = props;
const { DesignableBar } = useDesignable();
const filedSchema = useFieldSchema();
const form = useMemo(() => createForm(), []);
const [visible, setVisible] = useState(false);
const obj = flatten(form.values.filter || {});
const count = Object.values(obj).filter((i) => (Array.isArray(i) ? i.length : i)).length;
const icon = props.icon || 'FilterOutlined';
filedSchema.mapProperties((p) => {
properties[p.name] = p;
});
return (
<FilterContext.Provider value={{ visible, setVisible }}>
<SchemaComponent
schema={{
type: 'void',
properties: {
filter: {
type: 'object',
title: t('Filter'),
'x-component': 'Filter.Action',
},
},
}}
/>
</FilterContext.Provider>
);
});

View File

@ -1,2 +1,3 @@
export * from './Calendar'; export * from './Calendar';
export * from './context'; export * from './context';

View File

@ -0,0 +1,42 @@
import Database, { Collection, mockDatabase } from '@nocobase/database';
describe('string operator', () => {
let db: Database;
let User: Collection;
afterEach(async () => {
await db.close();
});
beforeEach(async () => {
db = mockDatabase({});
await db.clean({ drop: true });
User = db.collection({
name: 'users',
fields: [{ type: 'string', name: 'name' }],
});
await db.sync({
force: true,
});
});
it('should query with include operator', async () => {
const u1 = await db.getRepository('users').create({
values: {
name: 'names of u1',
},
});
const u1Res = await db.getRepository('users').findOne({
filter: {
'name.$includes': 'u1',
},
});
expect(u1Res.get('id')).toEqual(u1.get('id'));
});
});

View File

@ -1,4 +1,5 @@
import { Op, Sequelize } from 'sequelize'; import { Op, Sequelize } from 'sequelize';
import { isPg, isMySQL } from './utils';
const getFieldName = (ctx) => { const getFieldName = (ctx) => {
const fieldName = ctx.fieldName; const fieldName = ctx.fieldName;
@ -38,18 +39,6 @@ const emptyQuery = (ctx, operator: '=' | '>') => {
return `(select ${ifNull}(${funcName}(${fieldName}), 0) ${operator} 0)`; return `(select ${ifNull}(${funcName}(${fieldName}), 0) ${operator} 0)`;
}; };
const getDialect = (ctx) => {
return ctx.db.sequelize.getDialect();
};
const isPg = (ctx) => {
return getDialect(ctx) === 'postgres';
};
const isMySQL = (ctx) => {
return getDialect(ctx) === 'mysql';
};
export default { export default {
$match(value, ctx) { $match(value, ctx) {
const fieldName = getFieldName(ctx); const fieldName = getFieldName(ctx);

View File

@ -3,4 +3,5 @@ export default {
...require('./date').default, ...require('./date').default,
...require('./array').default, ...require('./array').default,
...require('./empty').default, ...require('./empty').default,
...require('./string').default,
}; };

View File

@ -0,0 +1,40 @@
import { isPg } from './utils';
import { Op } from 'sequelize';
export default {
$includes(value, ctx) {
return {
[isPg(ctx) ? Op.iLike : Op.like]: `%${value}%`,
};
},
$notIncludes(value, ctx) {
return {
[isPg(ctx) ? Op.notILike : Op.notLike]: `%${value}%`,
};
},
$startsWith(value, ctx) {
return {
[isPg(ctx) ? Op.iLike : Op.like]: `${value}%`,
};
},
$notStartsWith(value, ctx) {
return {
[isPg(ctx) ? Op.notILike : Op.notLike]: `${value}%`,
};
},
$endWith(value, ctx) {
return {
[isPg(ctx) ? Op.iLike : Op.like]: `%${value}`,
};
},
$notEndWith(value, ctx) {
return {
[isPg(ctx) ? Op.notILike : Op.notLike]: `%${value}`,
};
},
};

View File

@ -0,0 +1,13 @@
const getDialect = (ctx) => {
return ctx.db.sequelize.getDialect();
};
const isPg = (ctx) => {
return getDialect(ctx) === 'postgres';
};
const isMySQL = (ctx) => {
return getDialect(ctx) === 'mysql';
};
export { getDialect, isPg, isMySQL };