低代码平台的元数据查询是最核心的功能之一,而低代码平台面向的查询场景非常复杂,最普通的有:分页、排序、按需、字段条件以及关联对象条件、多查询操作符等。
条件分页排序查询+按需返回
基于上面场景,可以设计查询契约如下:
interface SortConfigItem {
field: string;
order: 'asc' | 'desc';
}
interface EntityConfig {
fields: string[];
entities: { code: string; fields: string[] }[];
}
interface FieldCondition {
code: string;
operation: SearchOperation;
value: any
}
interface ConditionsConfig {
fields?: Array<FieldCondition>;
entities?: Array<{ code: string, fields: FieldCondition[] }>
}
interface QueryConfig {
sort: SortConfigItem[],
entity: EntityConfig,
pageNo: number,
pageSize: number,
conditions: ConditionsConfig,
}
- pageNo/pageSize: 分页参数
- sort: 排序参数,可以支持多字段升序降序
- entity: 需要返回的字段,其中fields是当前元数据对象需要返回的字段,entities是当前元数据对象的关联对一
- conditions: 当前查询条件,其中fields表示查询的当前对象的字段条件,entities表示关联对象的字段条件
demo:
{
sort: [{ field: 'applys', order: 'asc' }],
entity: {
fields: ['classification', 'name', 'applys', 'remark', 'id'],
entities: [{ code: 'template', fields: ['name', 'id'] }],
},
pageNo: 1,
pageSize: 20,
conditions: {
fields: [
{ code: 'classification', operation: 'eq', value: ['1'] },
{ code: 'applys', operation: 'in', value: ['f', 'd'] },
],
entities: [
{
"code": "template",
"fields": [
{
"code": "name",
"operation": "eq",
"value": [
"模板1"
]
}
]
}
],
},
};
分组查询
分组查询是要支持多组条件or/and组合,并且需要支持多层嵌套。组在逻辑中的表示是一对括号,这里理解的一个难点是,怎样算一个组。
比如下面的组合,最佳处理是将a and b看成一组,c or d 看成一组,最终两个条件看成一组。组处理的原则是,最终形成的括号越少越好。
group的定义
group实际上是一个括号,并且该括号内部必定有一个操作符:or/and。该操作符定义了group内部多个条件之间的关系。
ts定义如下:
interface FieldsConditinGroup extends ConditionsConfig{
operation: SearchOperation.OR | SearchOperation.AND;
}
type QueryConditionGroup = {
operation: SearchOperation.OR | SearchOperation.AND;
groups: QueryConditionGroup[];
}| FieldsConditinGroup
interface QueryConditionConfig {
sort: SortConfigItem[];
entity: EntityConfig;
pageNo: number;
pageSize: number;
conditions: ConditionsConfig|QueryConditionGroup;
}
这里需要将原本普通查询条件放入一个group中,该group的操作符是and
demo:
const queryBody:QueryConfig = {
sort: [],
entity: {
fields: ['classification', 'name', 'applys', 'remark', 'id'],
entities: [{ code: 'template', fields: ['name', 'id'] }],
},
pageNo: 1,
pageSize: 20,
conditions: {
operation: SearchOperation.AND,
groups: [
{ operation: SearchOperation.AND, fields: [], entities: [] },
{
operation: SearchOperation.OR,
groups: [
{
operation: SearchOperation.AND,
fields: [
{ code: 'classification', operation: SearchOperation.EQUAL, value: ['1'] },
{ code: 'name', operation: SearchOperation.EQUAL, value: ['电脑'] },
],
},
{
operation: SearchOperation.AND,
fields: [
{ code: 'classification', operation: SearchOperation.EQUAL, value: ['2'] },
{ code: 'name', operation: SearchOperation.EQUAL, value: ['测试'] },
],
},
],
},
],
},
};
实践中的问题
实践中这套契约基本满足了所有场景的查询需求,甚至包括了统计查询,limit查询,并且导出接口也是基于以上契约。
然而,这套契约过于复杂。
在实践中,最理想的场景是,使用元数据sdk原生的查询接口+元数据低代码视图。但现实是,很多项目会组合低代码视图+元数据sdk+自身后端服务+外部接口。
因为低代码视图强绑定了这套复杂契约,那么如果查询接口要改为非sdk提供的查询,要么后端适配前端契约,要么前端通过拦截器将这套复杂契约改为对应接口的契约,这个过程相当痛苦,然而灵活和易用不可兼得。
另外,分组查询时的结构和普通查询的结构不一致,如果原本拦截器中按照普通查询的结构处理,当开启分组查询后,该拦截器代码需要随之更新。但这一点不管如何设计都无法避免,只能通过配置时的说明提示项目组该风险点。