From 04e9393deafe203df3ec0bd2347a25420ece6dce Mon Sep 17 00:00:00 2001 From: chenos Date: Sun, 27 Dec 2020 21:08:06 +0800 Subject: [PATCH] fix: association find and count --- packages/actions/src/actions/common.ts | 169 +++++++++++++++++++++---- 1 file changed, 146 insertions(+), 23 deletions(-) diff --git a/packages/actions/src/actions/common.ts b/packages/actions/src/actions/common.ts index 6e93452f2..e9fc85154 100644 --- a/packages/actions/src/actions/common.ts +++ b/packages/actions/src/actions/common.ts @@ -1,4 +1,4 @@ -import { Utils, Op } from 'sequelize'; +import { Utils, Op, Sequelize } from 'sequelize'; import _ from 'lodash'; import { Context, Next } from '.'; import { @@ -12,6 +12,136 @@ import { import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '@nocobase/resourcer'; import { filterByFields } from '../utils'; +async function hasManyGet(instances, options: any = {}) { + const where = {}; + + let Model = this.target; + let instance; + let values; + + if (!Array.isArray(instances)) { + instance = instances; + instances = undefined; + } + + options = { ...options }; + + if (this.scope) { + Object.assign(where, this.scope); + } + + if (instances) { + values = instances.map(_instance => _instance.get(this.sourceKey, { raw: true })); + + if (options.limit && instances.length > 1) { + options.groupedLimit = { + limit: options.limit, + on: this, // association + values + }; + + delete options.limit; + } else { + where[this.foreignKey] = { + [Op.in]: values + }; + delete options.groupedLimit; + } + } else { + where[this.foreignKey] = instance.get(this.sourceKey, { raw: true }); + } + + options.where = options.where ? + { [Op.and]: [where, options.where] } : + where; + + if (Object.prototype.hasOwnProperty.call(options, 'scope')) { + if (!options.scope) { + Model = Model.unscoped(); + } else { + Model = Model.scope(options.scope); + } + } + + if (Object.prototype.hasOwnProperty.call(options, 'schema')) { + Model = Model.schema(options.schema, options.schemaDelimiter); + } + + const results = await Model.findAndCountAll(options); + if (instance) return results; + + const result = {}; + for (const _instance of instances) { + result[_instance.get(this.sourceKey, { raw: true })] = []; + } + + for (const _instance of results) { + result[_instance.get(this.foreignKey, { raw: true })].push(_instance); + } + + return result; +} + +async function belongsToManyGet(instance, options) { + options = Utils.cloneDeep(options) || {}; + + const through = this.through; + let scopeWhere; + let throughWhere; + + if (this.scope) { + scopeWhere = { ...this.scope }; + } + + options.where = { + [Op.and]: [ + scopeWhere, + options.where + ] + }; + + if (Object(through.model) === through.model) { + throughWhere = {}; + throughWhere[this.foreignKey] = instance.get(this.sourceKey); + + if (through.scope) { + Object.assign(throughWhere, through.scope); + } + + //If a user pass a where on the options through options, make an "and" with the current throughWhere + if (options.through && options.through.where) { + throughWhere = { + [Op.and]: [throughWhere, options.through.where] + }; + } + + options.include = options.include || []; + options.include.push({ + association: this.oneFromTarget, + attributes: options.joinTableAttributes, + required: true, + paranoid: _.get(options.through, 'paranoid', true), + where: throughWhere + }); + } + + let model = this.target; + if (Object.prototype.hasOwnProperty.call(options, 'scope')) { + if (!options.scope) { + model = model.unscoped(); + } else { + model = model.scope(options.scope); + } + } + + if (Object.prototype.hasOwnProperty.call(options, 'schema')) { + model = model.schema(options.schema, options.schemaDelimiter); + } + + return model.findAndCountAll(options); +} + + /** * 查询数据列表 * @@ -53,31 +183,24 @@ export async function list(ctx: Context, next: Next) { if (!(associated instanceof AssociatedModel)) { throw new Error(`${associatedName} associated model invalid`); } - const getAccessor = resourceField.getAccessors().get; - const countAccessor = resourceField.getAccessors().count; + // const getAccessor = resourceField.getAccessors().get; + // const countAccessor = resourceField.getAccessors().count; options.scope = options.scopes||[]; - const rows = await associated[getAccessor]({ - joinTableAttributes: [], - ...options, - context: ctx, - }); - const associatedOptions = _.omit(options, [ - 'attributes', - 'limit', - 'offset', - 'order' - ]); - if (associatedOptions.include) { - associatedOptions.include = associatedOptions.include.map(includeOptions => { - includeOptions.attributes = []; - return includeOptions; + const association = AssociatedModel.associations[resourceField.options.name]; + if (resourceField instanceof BELONGSTOMANY) { + data = await belongsToManyGet.call(association, associated, { + joinTableAttributes: [], + ...options, + context: ctx, + }); + } + if (resourceField instanceof HASMANY) { + data = await hasManyGet.call(association, associated, { + joinTableAttributes: [], + ...options, + context: ctx, }); } - const count = await associated[countAccessor]({ ...associatedOptions, context: ctx }); - data = { - rows, - count, - }; } else { Model = ctx.db.getModel(resourceName); options = Model.parseApiJson({