2020-12-28 21:08:13 +08:00
|
|
|
import { Utils, Sequelize, Op } from 'sequelize';
|
2020-10-24 15:34:43 +08:00
|
|
|
import Model, { ModelCtor } from './model';
|
|
|
|
import _ from 'lodash';
|
2020-12-28 21:08:13 +08:00
|
|
|
import op from './op';
|
|
|
|
import Database from './database';
|
2020-10-24 15:34:43 +08:00
|
|
|
|
|
|
|
interface ToWhereContext {
|
2020-11-23 16:49:46 +08:00
|
|
|
model?: ModelCtor<Model> | Model | typeof Model;
|
2020-10-24 15:34:43 +08:00
|
|
|
associations?: any;
|
|
|
|
dialect?: string;
|
2020-12-28 21:08:13 +08:00
|
|
|
database?: Database;
|
2020-10-24 15:34:43 +08:00
|
|
|
ctx?: any;
|
2020-12-28 21:08:13 +08:00
|
|
|
prefix?: any;
|
2020-10-24 15:34:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export function toWhere(options: any, context: ToWhereContext = {}) {
|
2020-11-18 10:35:37 +08:00
|
|
|
if (options === null || typeof options !== 'object') {
|
2020-10-24 15:34:43 +08:00
|
|
|
return options;
|
|
|
|
}
|
2020-11-18 10:35:37 +08:00
|
|
|
if (Array.isArray(options)) {
|
|
|
|
return options.map((item) => toWhere(item, context));
|
|
|
|
}
|
2020-12-29 14:53:39 +08:00
|
|
|
const { prefix, model, associations = {}, ctx, dialect, database } = context;
|
2020-10-24 15:34:43 +08:00
|
|
|
const items = {};
|
|
|
|
// 先处理「点号」的问题
|
|
|
|
for (const key in options) {
|
|
|
|
_.set(items, key, options[key]);
|
|
|
|
}
|
2020-12-28 21:08:13 +08:00
|
|
|
let values = {};
|
2020-10-24 15:34:43 +08:00
|
|
|
for (const key in items) {
|
2020-12-28 21:08:13 +08:00
|
|
|
const childPreifx = prefix ? `${prefix}.${key}` : key;
|
2020-10-24 15:34:43 +08:00
|
|
|
if (associations[key]) {
|
|
|
|
values['$__include'] = values['$__include'] || {}
|
|
|
|
values['$__include'][key] = toWhere(items[key], {
|
|
|
|
...context,
|
2020-12-28 21:08:13 +08:00
|
|
|
prefix: childPreifx,
|
2020-11-23 16:49:46 +08:00
|
|
|
model: associations[key].target,
|
2020-10-24 15:34:43 +08:00
|
|
|
associations: associations[key].target.associations,
|
|
|
|
});
|
|
|
|
}
|
2020-11-23 16:49:46 +08:00
|
|
|
else if (model && model.options.scopes && model.options.scopes[key]) {
|
2020-10-24 15:34:43 +08:00
|
|
|
values['$__scopes'] = values['$__scopes'] || [];
|
2020-11-23 16:49:46 +08:00
|
|
|
const scope = model.options.scopes[key];
|
2020-10-24 15:34:43 +08:00
|
|
|
if (typeof scope === 'function') {
|
|
|
|
values['$__scopes'].push({ method: [key, items[key], ctx] });
|
|
|
|
} else {
|
|
|
|
values['$__scopes'].push(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-11-18 10:35:37 +08:00
|
|
|
// TODO: to fix same op key as field name
|
2020-12-28 21:08:13 +08:00
|
|
|
const opKey = op.get(key);
|
|
|
|
let k;
|
|
|
|
switch (typeof opKey) {
|
|
|
|
case 'function':
|
2020-12-31 21:04:03 +08:00
|
|
|
const name = model ? model.name : '';
|
2021-01-13 16:23:15 +08:00
|
|
|
const result = opKey(items[key], {
|
|
|
|
ctx,
|
2020-12-29 14:53:39 +08:00
|
|
|
model,
|
|
|
|
database,
|
|
|
|
fieldPath: name ? `${name}.${prefix}` : prefix,
|
|
|
|
});
|
2020-12-28 21:08:13 +08:00
|
|
|
if (result.constructor.name === 'Literal') {
|
|
|
|
values['$__literals'] = values['$__literals'] || [];
|
|
|
|
values['$__literals'].push(result);
|
|
|
|
} else {
|
|
|
|
Object.assign(values, result);
|
|
|
|
}
|
|
|
|
// console.log(result.constructor.name === 'Literal');
|
|
|
|
continue;
|
|
|
|
case 'undefined':
|
|
|
|
k = key;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
k = opKey;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
values[k] = toWhere(items[key], {
|
|
|
|
...context,
|
|
|
|
prefix: op.has(key) ? prefix : childPreifx,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (values['$__literals']) {
|
|
|
|
const $__literals = _.cloneDeep(values['$__literals']);
|
|
|
|
delete values['$__literals'];
|
|
|
|
console.log(Object.keys(values));
|
|
|
|
return {
|
|
|
|
[Op.and]: [
|
|
|
|
...$__literals,
|
|
|
|
values,
|
|
|
|
],
|
2020-10-24 15:34:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return values;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ToIncludeContext {
|
2020-11-23 16:49:46 +08:00
|
|
|
model?: ModelCtor<Model> | Model | typeof Model;
|
2020-10-24 15:34:43 +08:00
|
|
|
sourceAlias?: string;
|
|
|
|
associations?: any;
|
|
|
|
dialect?: string;
|
2020-12-28 21:08:13 +08:00
|
|
|
database?: Database;
|
2020-10-24 15:34:43 +08:00
|
|
|
ctx?: any
|
|
|
|
}
|
|
|
|
|
2020-12-08 14:27:51 +08:00
|
|
|
export function toOrder(sort: string | string[], model: any): string[][] {
|
|
|
|
if (sort && typeof sort === 'string') {
|
|
|
|
sort = sort.split(',');
|
|
|
|
}
|
|
|
|
|
|
|
|
const order = [];
|
|
|
|
|
|
|
|
if (Array.isArray(sort) && sort.length > 0) {
|
|
|
|
sort.forEach(key => {
|
|
|
|
if (Array.isArray(key)) {
|
|
|
|
order.push(key);
|
|
|
|
} else {
|
|
|
|
const direction = key[0] === '-' ? 'DESC' : 'ASC';
|
|
|
|
const keys = key.replace(/^-/, '').split('.');
|
|
|
|
const field = keys.pop();
|
|
|
|
const by = [];
|
|
|
|
let associationModel = model;
|
|
|
|
for (let i = 0; i < keys.length; i++) {
|
|
|
|
const association = model.associations[keys[i]];
|
|
|
|
if (association && association.target) {
|
|
|
|
associationModel = association.target;
|
|
|
|
by.push(associationModel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
order.push([...by, field, direction]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return order;
|
|
|
|
}
|
|
|
|
|
2020-10-24 15:34:43 +08:00
|
|
|
export function toInclude(options: any, context: ToIncludeContext = {}) {
|
2020-12-24 07:21:46 +08:00
|
|
|
function makeFields(key: string) {
|
2020-11-23 16:49:46 +08:00
|
|
|
if (!Array.isArray(items[key])) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
items[key].forEach(field => {
|
2020-12-08 14:27:51 +08:00
|
|
|
// 按点分隔转化为数组
|
2020-12-24 07:21:46 +08:00
|
|
|
const [col, ...arr]: string[] = Array.isArray(field) ? Utils.cloneDeep(field) : field.split('.');
|
2020-11-23 16:49:46 +08:00
|
|
|
// 内嵌的情况
|
|
|
|
if (arr.length > 0) {
|
|
|
|
if (!children.has(col)) {
|
|
|
|
children.set(col, {
|
|
|
|
fields: {
|
|
|
|
only: [],
|
|
|
|
except: [],
|
|
|
|
appends: [],
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
children.get(col).fields[key].push(arr);
|
|
|
|
return;
|
|
|
|
}
|
2020-12-24 07:21:46 +08:00
|
|
|
if (key !== 'except') {
|
|
|
|
// 关系字段
|
|
|
|
if (associations[col]) {
|
|
|
|
const includeItem: any = {
|
|
|
|
association: col,
|
|
|
|
};
|
|
|
|
if (includeWhere[col]) {
|
|
|
|
includeItem.where = includeWhere[col];
|
|
|
|
}
|
|
|
|
include.set(col, includeItem);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// 计数字段
|
|
|
|
const matches: Array<any> = col.match(/(.+)_count$/);
|
|
|
|
if (matches && associations[matches[1]]) {
|
|
|
|
attributes[key].push(model.withCountAttribute({
|
|
|
|
association: matches[1],
|
|
|
|
sourceAlias: sourceAlias
|
|
|
|
}));
|
|
|
|
return;
|
2020-11-23 16:49:46 +08:00
|
|
|
}
|
|
|
|
} else {
|
2020-12-24 07:21:46 +08:00
|
|
|
if (!attributes.except) {
|
|
|
|
attributes.except = [];
|
|
|
|
}
|
2020-11-23 16:49:46 +08:00
|
|
|
}
|
2020-12-24 07:21:46 +08:00
|
|
|
attributes[key].push(col);
|
2020-11-23 16:49:46 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-12-08 14:27:51 +08:00
|
|
|
const { fields = [], filter } = options;
|
2020-12-29 14:53:39 +08:00
|
|
|
const { model, sourceAlias, associations = {}, ctx, database, dialect } = context;
|
2020-10-24 15:34:43 +08:00
|
|
|
|
|
|
|
let where = options.where || {};
|
|
|
|
|
2020-12-08 14:27:51 +08:00
|
|
|
if (filter) {
|
|
|
|
where = toWhere(filter, {
|
2020-11-23 16:49:46 +08:00
|
|
|
model,
|
2020-10-24 15:34:43 +08:00
|
|
|
associations,
|
|
|
|
ctx,
|
2020-12-29 14:53:39 +08:00
|
|
|
database,
|
2020-10-24 15:34:43 +08:00
|
|
|
}) || {};
|
|
|
|
}
|
|
|
|
|
2021-03-28 13:34:51 +08:00
|
|
|
const includeWhere = Utils.cloneDeep(where.$__include || {});
|
|
|
|
const scopes = Utils.cloneDeep(where.$__scopes || []);
|
2020-10-24 15:34:43 +08:00
|
|
|
|
|
|
|
delete where.$__include;
|
|
|
|
delete where.$__scopes;
|
|
|
|
|
|
|
|
const attributes = {
|
|
|
|
only: [],
|
|
|
|
except: [],
|
|
|
|
appends: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
const include = new Map();
|
|
|
|
const children = new Map();
|
|
|
|
|
|
|
|
const items = Array.isArray(fields) ? { only: fields } : fields;
|
2020-11-23 16:49:46 +08:00
|
|
|
items.appends = items.appends || [];
|
2021-03-28 13:34:51 +08:00
|
|
|
|
2020-11-23 16:49:46 +08:00
|
|
|
makeFields('only');
|
|
|
|
makeFields('appends');
|
2020-12-24 07:21:46 +08:00
|
|
|
makeFields('except');
|
2020-10-24 15:34:43 +08:00
|
|
|
|
|
|
|
for (const whereKey in includeWhere) {
|
|
|
|
if (children.has(whereKey)) {
|
|
|
|
children.get(whereKey).where = includeWhere[whereKey];
|
|
|
|
} else {
|
|
|
|
children.set(whereKey, {
|
|
|
|
association: whereKey,
|
|
|
|
fields: [],
|
|
|
|
where: includeWhere[whereKey],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const [key, child] of children) {
|
|
|
|
const result = toInclude(child, {
|
|
|
|
...context,
|
2020-11-23 16:49:46 +08:00
|
|
|
model: associations[key].target,
|
2020-10-24 15:34:43 +08:00
|
|
|
sourceAlias: key,
|
|
|
|
associations: associations[key].target.associations,
|
|
|
|
});
|
|
|
|
const item: any = {
|
|
|
|
association: key,
|
|
|
|
}
|
|
|
|
if (result.attributes) {
|
|
|
|
item.attributes = result.attributes;
|
|
|
|
}
|
|
|
|
if (result.include) {
|
|
|
|
item.include = result.include;
|
|
|
|
}
|
|
|
|
if (result.where) {
|
|
|
|
item.where = result.where;
|
|
|
|
}
|
|
|
|
if (result.scopes) {
|
|
|
|
item.model = associations[key].target.scope(result.scopes);
|
|
|
|
}
|
2020-12-24 07:21:46 +08:00
|
|
|
// 解决同时有关联和关联的子级关联时,关联的 attribute 被设置为空数组的问题
|
|
|
|
// ['user.profile.age', 'user.status', 'user', 'title', 'status']
|
|
|
|
if (include.has(key) && Array.isArray(item.attributes) && !item.attributes.length) {
|
|
|
|
delete item.attributes;
|
|
|
|
}
|
2020-10-24 15:34:43 +08:00
|
|
|
include.set(key, item);
|
|
|
|
}
|
|
|
|
|
2020-11-23 16:49:46 +08:00
|
|
|
const data: any = {};
|
2020-10-24 15:34:43 +08:00
|
|
|
|
|
|
|
// 存在黑名单时
|
2020-12-24 07:21:46 +08:00
|
|
|
if (attributes.except.length) {
|
2020-10-24 15:34:43 +08:00
|
|
|
data.attributes = {
|
|
|
|
exclude: attributes.except,
|
|
|
|
};
|
2020-11-23 16:49:46 +08:00
|
|
|
if (attributes.appends.length) {
|
|
|
|
data.attributes.include = attributes.appends;
|
|
|
|
}
|
2020-10-24 15:34:43 +08:00
|
|
|
}
|
|
|
|
// 存在白名单时
|
2020-12-24 07:21:46 +08:00
|
|
|
else if (attributes.only.length) {
|
2020-10-24 15:34:43 +08:00
|
|
|
data.attributes = [...attributes.only, ...attributes.appends];
|
|
|
|
}
|
|
|
|
// 只有附加字段时
|
2020-12-24 07:21:46 +08:00
|
|
|
else if (attributes.appends.length) {
|
2020-10-24 15:34:43 +08:00
|
|
|
data.attributes = {
|
|
|
|
include: attributes.appends,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-12-24 07:21:46 +08:00
|
|
|
if (include.size) {
|
2020-10-24 15:34:43 +08:00
|
|
|
if (!data.attributes) {
|
|
|
|
data.attributes = [];
|
|
|
|
}
|
|
|
|
data.include = Array.from(include.values());
|
2020-11-23 16:49:46 +08:00
|
|
|
data.distinct = true;
|
2020-10-24 15:34:43 +08:00
|
|
|
}
|
|
|
|
|
2020-12-24 07:21:46 +08:00
|
|
|
if (Reflect.ownKeys(where).length) {
|
2020-10-24 15:34:43 +08:00
|
|
|
data.where = where;
|
|
|
|
}
|
|
|
|
|
2020-12-24 07:21:46 +08:00
|
|
|
if (scopes.length) {
|
2020-10-24 15:34:43 +08:00
|
|
|
data.scopes = scopes;
|
|
|
|
}
|
|
|
|
|
2020-12-08 14:27:51 +08:00
|
|
|
const order = toOrder(options.sort, model);
|
|
|
|
|
2020-12-24 07:21:46 +08:00
|
|
|
if (order.length) {
|
2020-10-24 15:34:43 +08:00
|
|
|
data.order = order;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2020-12-11 15:41:03 +08:00
|
|
|
export function whereCompare(a: any, b: any): boolean {
|
|
|
|
return _.isEqual(a, b);
|
|
|
|
}
|
|
|
|
|
2020-10-24 15:34:43 +08:00
|
|
|
export function requireModule(module: any) {
|
|
|
|
if (typeof module === 'string') {
|
|
|
|
module = require(module);
|
|
|
|
}
|
|
|
|
if (typeof module !== 'object') {
|
|
|
|
return module;
|
|
|
|
}
|
|
|
|
return module.__esModule ? module.default : module;
|
|
|
|
}
|
2020-12-16 20:47:10 +08:00
|
|
|
|
|
|
|
export function isNumber(num) {
|
|
|
|
if (typeof num === 'number') {
|
|
|
|
return num - num === 0;
|
|
|
|
}
|
|
|
|
if (typeof num === 'string' && num.trim() !== '') {
|
|
|
|
return Number.isFinite ? Number.isFinite(+num) : isFinite(+num);
|
|
|
|
}
|
|
|
|
return false;
|
2020-12-24 07:21:46 +08:00
|
|
|
};
|