UNPKG

alaska

Version:

Componentized and pluggable web framework for Node.js

1,217 lines (1,097 loc) 36 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* eslint no-proto:0 no-unused-expressions:0 */ var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _collie = require('collie'); var _collie2 = _interopRequireDefault(_collie); var _mongoose = require('mongoose'); var _mongoose2 = _interopRequireDefault(_mongoose); var _alaska = require('./alaska'); var _alaska2 = _interopRequireDefault(_alaska); var _utils = require('./utils'); var utils = _interopRequireWildcard(_utils); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const { Schema } = _mongoose2.default; // $Flow _mongoose2.default.Promise = global.Promise; function panic() { throw new Error('Can not call the function when Model has been registered.'); } function processScope(fields, Model) { if (typeof fields === 'object') return fields; let keys = {}; fields.split(' ').map(s => s.trim()).filter(s => s).forEach(s => { if (s === '*') { Object.keys(Model.defaultScope).forEach(f => { keys[f] = 1; }); } else if (s[0] === '-') { s = s.substr(1); if (!Model.defaultScope[s] && !Model.fields[s]) { throw new Error(`Can not find field ${Model.path}.scopes.${s} when process scopes`); } delete keys[s]; } else if (s[0] === ':') { s = s.substr(1); let scope = Model.scopes[s]; if (!scope) { throw new Error(`Can not find scope ${Model.path}.scopes.${s} when process scopes`); } if (typeof scope === 'object') { Object.assign(keys, scope); } else { throw new Error(`Can not init scope, ${Model.path}.scopes.${s} should be Object`); } } else if (s[0] === '_') { s = s.substr(1); if (!Model.fields[s]) { throw new Error(`Can not find field ${Model.path}.scopes.${s} when process scopes`); } keys[s] = 1; keys['_' + s] = 1; } else { if (!Model.defaultScope[s] && !Model.fields[s] && (!Model.virtuals || !Object.getOwnPropertyDescriptor(Model.virtuals, s).get)) { throw new Error(`Can not find field ${Model.path}.scopes.${s} when process scopes`); } keys[s] = 1; } }); return keys; } function processSelect(obj, Model) { if (obj.select) { obj.select = processScope(obj.select, Model); } if (obj.scopes) { Object.keys(obj.scopes).forEach(i => { obj.scopes[i] = processScope(obj.scopes[i], Model); }); } } function processPopulation(query, pop, Model, scopeKey) { //判断scope是否不需要返回此path if (Model.scopes[scopeKey] && !Model.scopes[scopeKey][pop.path]) return null; let config = pop; if (pop.autoSelect === false && pop.select) { config = _lodash2.default.omit(pop, 'select'); } else if (pop.autoSelect !== false && pop.scopes && pop.scopes[scopeKey]) { config = Object.assign({}, pop, { select: pop.scopes[scopeKey] }); } query.populate(config); return config; } function createRelationshipQuery(r, res, scopeKey) { let Ref = r.ref; let q = Ref.find({ [r.path]: res._id }); let sort = Ref.defaultSort; if (r.options) { sort = r.options.sort || sort; if (r.options.limit) { q.limit(r.options.limit); } } if (sort) { q.sort(sort); } if (r.filters) { q.where(r.filters); } if (Ref.autoSelect) { //自动select字段,优化查询性能 if (r.scopes && r.scopes[scopeKey]) { q.select(r.scopes[scopeKey]); } else if (r.select) { q.select(r.select); } else if (Ref.scopes[scopeKey]) { q.select(Ref.scopes[scopeKey]); } } let popConfig = {}; if (r.populations) { _lodash2.default.forEach(r.populations, (pop, key) => { if (Ref.populations[key]) { popConfig[key] = processPopulation(q, Object.assign({ path: key }, Ref.populations[key], pop), Ref, scopeKey); } }); } return q.then(list => { if (list && list.length) { _lodash2.default.forEach(popConfig, pop => { if (pop.select) { _lodash2.default.forEach(list, record => { if (!record[pop.path]) return; [].concat(record[pop.path]).forEach(tmp => { tmp.___fields = pop.select; }); }); } }); } res[r.key] = list; }); } /** * Data * @type {{pick: (function()), omit: (function())}} */ const Data = { pick(...args) { let { getRecord } = this; let data = _lodash2.default.pick(this, ...args) || {}; Object.setPrototypeOf(data, Data); if (getRecord) { data.getRecord = getRecord; } return data; }, omit(...args) { let { getRecord } = this; let data = _lodash2.default.omit(this, ...args) || {}; Object.setPrototypeOf(data, Data); if (getRecord) { data.getRecord = getRecord; } return data; } }; function objectToData(value, fields) { if (!value) { return value; } if (value instanceof Date) { //时间 } else if (value instanceof Array) { //数组数据 return value.map(val => { if (val === 'object') { return objectToData(val, fields); } return val; }); } else if (value.data && typeof value.data === 'function') { //如果也有data 函数,判定为document value = value.data(fields); } else { //无法判断 //console.log(value); } return value; } /** * @class Model * @extends mongoose.Model */ class Model { // eslint-disable-next-line constructor(doc, fields, skipId) { throw new Error('Can not initialize a Model before register.'); } /** * 注册前置钩子 * @param {string} action 动作名称,Init|Validate|Save|Remove * @param {function} fn */ static pre(action, fn) { this._pre || (this._pre = {}); this._pre[action] || (this._pre[action] = []); this._pre[action].push(fn); } /** * 注册后置钩子 * @param {string} action 动作名称,Init|Validate|Save|Remove * @param {function} fn */ static post(action, fn) { this._post || (this._post = {}); this._post[action] || (this._post[action] = []); this._post[action].push(fn); } /** * 注册underscore方法 * @param {string} field 绑定的字段 * @param {string} name 方法名 * @param {Function} fn 方法 */ static underscoreMethod(field, name, fn) { delete this.__methods; this._underscore || (this._underscore = {}); this._underscore[field] || (this._underscore[field] = {}); this._underscore[field][name] = fn; } /** * 注册 */ static async register() { const { service } = this; // $Flow const model = this; const modelName = model.modelName; model._fields = {}; function loadFieldConfig(fieldTypeName) { let config = service.getConfig(fieldTypeName, undefined, true); if (!config) { return {}; } if (config.type && config.type !== fieldTypeName) { let otherConfig = loadFieldConfig(config.type); return Object.assign({}, config, otherConfig); } return _lodash2.default.clone(config); } try { const { db } = service; model.db = db; /** * 模型所属服务 * @type {Service} */ model.service = service; /** * 标识模型对象 * @type {boolean} */ model.classOfModel = true; model.id = utils.nameToKey(modelName); model.key = service.id + '.' + model.id; model.path = service.id + '.' + modelName; _lodash2.default.defaults(model, { titleField: 'title', userField: 'user', api: {}, searchFields: '', defaultFilters: null, defaultSort: '', defaultColumns: '', label: modelName, actions: {}, groups: {}, schemaOptions: {} }); if (!model.groups.default) { model.groups.default = {}; } //自动查询仅仅需要的字段 if (model.autoSelect !== false) { model.autoSelect = true; } if (this._pre && this._pre.register) { await _collie2.default.compose(this._pre.register, [], this); delete this._pre.register; } if (typeof this.preRegister === 'function') { await this.preRegister(); } const schema = new Schema({}, _extends({}, model.schemaOptions, { collection: model.collection || (service.getConfig('dbPrefix') || '') + model.id.replace(/-/g, '_') })); model.schema = schema; /** * init fields */ try { if (!model.fields) { throw new Error(modelName + ' model has no fields.'); } let keys = Object.keys(model.fields); if (!model.fields._id) { keys.unshift('_id'); model.fields._id = { type: 'id', view: '' }; } model.defaultScope = {}; //将Model字段注册到Mongoose.Schema中 keys.forEach(path => { try { let options = _lodash2.default.clone(model.fields[path]); if (!options.type) { /** * eg. * user : { * ref: User * } */ if (options.ref) { options.type = 'relationship'; if (Array.isArray(options.ref)) { // eslint-disable-next-line prefer-destructuring options.ref = options.ref[0]; options.multi = true; } } else { throw new Error(modelName + '.' + path + ' field type not specified'); } } options.path = path; let orgType = options.type; let FieldClass = null; if (typeof options.type === 'object' && options.type.classOfField) { FieldClass = options.type; } else { let fieldTypeName = ''; if (options.type === String) { fieldTypeName = 'alaska-field-text'; } else if (options.type === Date) { fieldTypeName = 'alaska-field-datetime'; } else if (options.type === Boolean) { fieldTypeName = 'alaska-field-checkbox'; } else if (options.type === Object) { fieldTypeName = 'alaska-field-mixed'; } else if (options.type === Number) { fieldTypeName = 'alaska-field-number'; } else if (typeof options.type === 'string') { fieldTypeName = 'alaska-field-' + options.type; } else { throw new Error(`Unsupported field type for ${modelName}.${path}`); } delete options.type; Object.assign(options, loadFieldConfig(fieldTypeName)); if (options.type && typeof options.type === 'string') { fieldTypeName = options.type; } FieldClass = _alaska2.default.modules.fields[fieldTypeName]; if (!FieldClass) { console.error(`Field type '${fieldTypeName}' not found!`); } options.type = FieldClass; } options.label = options.label || path.toUpperCase(); let field = new FieldClass(options, schema, model); model._fields[path] = field; if (path !== '_id' || orgType !== 'id') { // 默认 _id 不需要执行 initSchema,否则创建数据将失败 field.initSchema(); } if (!field.private) { model.defaultScope[path] = true; } } catch (e) { console.error(`${service.id}.${modelName}.fields.${path} init failed!`); throw e; } }); } catch (e) { console.error(`${model.path} init fields failed!`); throw e; } /** * init virtual fields */ try { model._virtuals = {}; if (model.virtuals) { Object.keys(model.virtuals).forEach(path => { model._virtuals[path] = true; let descriptor = Object.getOwnPropertyDescriptor(model.virtuals, path); if (descriptor.get) { model.defaultScope[path] = 1; schema.virtual(path).get(descriptor.get); } if (descriptor.set) { schema.virtual(path).set(descriptor.set); } }); } } catch (e) { console.error(`${model.path} init virtual fields failed!`); throw e; } let needRef = false; /** * init relationships */ try { let relationships = {}; if (model.relationships) { _lodash2.default.forEach(model.relationships, (r, key) => { //'Model' let Ref = r.ref || service.error(`${model.path}.relationships.${key}.ref is undefined`); if (typeof Ref === 'string') { Ref = service.getModel(r.ref); } r.key = key; r.ref = Ref; if (!r.private) { model.defaultScope[key] = 1; } if (r.populations) { //有深层嵌套 populations needRef = true; } relationships[key] = r; }); } model.relationships = relationships; } catch (e) { console.error(`${model.path} init relationships failed!`); } /** * init populations */ try { let populations = {}; _lodash2.default.forEach(model.populations, (p, key) => { if (!p.path && typeof key === 'string') { p.path = key; } if (p.filters) { p.match = p.filters; } let field = model._fields[p.path]; if (!field) { throw new Error(`${service.id}.${modelName}.populations error, can not populate '${p.path}'`); } p.model = model._fields[p.path].ref; populations[p.path] = p; if (p.select || p.scopes || p.populations) { needRef = true; } }); model.populations = populations; } catch (e) { console.error(`${model.path} init populations failed!`); } if (needRef) { service.pre('loadSleds', () => { _lodash2.default.forEach(model.populations, p => { let Ref = model._fields[p.path].ref; p.ref = Ref; p.autoSelect = Ref.autoSelect; processSelect(p, Ref); //有多层嵌套 populations if (p.populations && p.model) { _lodash2.default.forEach(p.populations, (item, k) => { processSelect(item, p.model._fields[k].ref); }); } }); _lodash2.default.forEach(model.relationships, r => { if (!r.populations) return; _lodash2.default.forEach(r.populations, (item, k) => { processSelect(item, r.ref._fields[k].ref); }); }); }); } if (!model.defaultColumns) { model.defaultColumns = ['_id']; if (model.titleField && model._fields[model.titleField]) { model.defaultColumns.push(model.titleField); } if (model._fields.createdAt) { model.defaultColumns.push('createdAt'); } } else if (typeof model.defaultColumns === 'string') { model.defaultColumns = model.defaultColumns.split(' ').filter(f => f); } if (model.searchFields) { if (typeof model.searchFields === 'string') { model.searchFields = model.searchFields.split(' ').filter(k => k && model._fields[k]); } } else { model.searchFields = []; } if (model.scopes) { if (model.scopes['*']) { model.defaultScope = processScope(model.scopes['*'], model); } _lodash2.default.forEach(model.scopes, (scopeConfig, scopeName) => { if (scopeName === '*') return; model.scopes[scopeName] = processScope(scopeConfig, model); }); } else { model.scopes = {}; } if (!model.scopes.show) { model.scopes.show = model.defaultScope; } model._pre || (model._pre = {}); model._post || (model._post = {}); ['Init', 'Validate', 'Save', 'Remove'].forEach(Action => { // $Flow let action = Action.toLowerCase(); { let preHooks = model._pre[action] || []; if (model.prototype['pre' + Action]) { preHooks.push(model.prototype['pre' + Action]); delete model.prototype['pre' + Action]; } if (preHooks.length) { schema.pre(action, function (next) { try { let promise = _collie2.default.compose(preHooks, [], this); promise.then(() => { if (action === 'save' && this.__modifiedPaths) { this.__modifiedPaths = this.modifiedPaths(); } next(); }, next); } catch (error) { next(error); } }); } else if (action === 'save') { schema.pre(action, function (next) { try { if (this.__modifiedPaths) { this.__modifiedPaths = this.modifiedPaths(); } next(); } catch (error) { next(error); } }); } delete model._pre[action]; } { let postHooks = []; if (model.prototype['post' + Action]) { postHooks.push(model.prototype['post' + Action]); delete model.prototype['post' + Action]; } if (model._post[action]) { postHooks = postHooks.concat(model._post[action]); } if (postHooks.length) { schema.post(action, function () { try { let promise = _collie2.default.compose(postHooks, [], this); promise.catch(error => { console.error(error.stack); }); } catch (error) { console.error(error.stack); } }); } delete model._post[action]; } }); schema.virtual('_').get(function () { if (!this.__methods) { this.__methods = utils.bindMethods(model._underscore || {}, this); } return this.__methods; }); /** * 返回格式化数据 * @param {string} [scope] * @returns {Data} */ schema.methods.data = function (scope) { let doc = { id: this.id }; let fields = model.defaultScope; if (scope) { if (typeof scope === 'object') { fields = scope; scope = null; } else if (model.scopes[scope]) { fields = model.scopes[scope]; } } _lodash2.default.forEach(fields, (any, key) => { if (key[0] === '_') return; if (!model._virtuals[key]) { if (model._fields[key] && (model._fields[key].private || !this.isSelected(key))) return; if (!model._fields[key] && (!model.relationships[key] || model.relationships[key].private)) return; } if (fields['_' + key]) return; if (this._[key] && this._[key].data) { doc[key] = this._[key].data(); } else { let value = this[key]; if (value && typeof value === 'object') { let p = model.populations[key]; let _fields = value.___fields; if (!_fields && p) { if (p.scopes && p.scopes[scope]) { _fields = p.scopes[scope]; } else if (p.select) { _fields = p.select; } } doc[key] = objectToData(value, _fields); } else if (value === undefined) { let fieldConfig = model._fields[key]; if (fieldConfig && fieldConfig.defaultValue !== undefined) { doc[key] = fieldConfig.defaultValue; } } else { doc[key] = value; } } }); Object.setPrototypeOf(doc, Data); doc.getRecord = () => this; return doc; }; Object.getOwnPropertyNames(model.prototype).forEach(key => { if (key === 'constructor') return; schema.methods[key] = model.prototype[key]; delete model.prototype[key]; }); { let keys = Object.keys(model._pre); if (keys.length) { console.warn('Unknown pre hooks ' + keys.toString() + ' of ' + modelName); } } { let keys = Object.keys(model._post); if (keys.length) { console.warn('Unknown post hooks ' + keys.toString() + ' of ' + modelName); } } model.registered = true; // $Flow model.register = panic; // $Flow model.pre = panic; // $Flow model.post = panic; delete model._pre; ['paginateByContext', 'listByContext', 'showByContext', 'createFilters', 'createFiltersByContext', 'paginate', 'fromObject', 'fromObjectArray', 'toObjectArray'].forEach(key => { // $Flow model[key] = Model[key]; }); //register let MongooseModel = db.model(modelName, schema); /** * 原始Mongoose模型 * @type mongoose.Model */ // $Flow model.MongooseModel = MongooseModel; Object.setPrototypeOf(model, MongooseModel); // $Flow Object.setPrototypeOf(model.prototype, MongooseModel.prototype); if (typeof this.postRegister === 'function') { await this.postRegister(); } if (this._post && this._post.register) { await _collie2.default.compose(this._post.register, [], this); delete this._post.register; } delete model._post; } catch (e) { console.error(`${service.id}.${modelName}.register failed!`); throw e; } } /** * 创建查询过滤器 * @param {string} [search] * @param {object|json} [filters] */ static createFilters(search, filters) { if (filters && typeof filters === 'string') { filters = JSON.parse(filters); } let result = {}; // $Flow let model = this; if (search && model.searchFields.length) { let searchFilters = []; let rx; let keywords = search.split(' '); if (keywords.length > 1) { rx = new RegExp(_lodash2.default.map(keywords, keyword => utils.escapeRegExp(keyword)).join('|'), 'i'); } else { rx = new RegExp(utils.escapeRegExp(search), 'i'); } // $Flow _lodash2.default.forEach(model.searchFields, key => { searchFilters.push({ [key]: rx }); }); if (searchFilters.length > 1) { result = { $or: searchFilters }; } else { // eslint-disable-next-line prefer-destructuring result = searchFilters[0]; } } // $Flow _lodash2.default.forEach(filters, (value, key) => { if (model._fields[key] && model._fields[key].createFilter) { let filter = model._fields[key].createFilter(value, result); if (filter !== undefined) { result[key] = filter; } } }); if (filters && filters.$or) { result.$or = filters.$or; } return result; } /** * 通过ctx创建过滤器 * @param {Alaska$Context} ctx * @param {Object} [state] */ static async createFiltersByContext(ctx, state) { // $Flow state = _lodash2.default.defaultsDeep({}, state, ctx.state); // $Flow let model = this; let filters = model.createFilters((state.search || ctx.query._search || '').trim(), state.filters || ctx.query); let { defaultFilters } = model; if (defaultFilters) { if (typeof defaultFilters === 'function') { defaultFilters = defaultFilters(ctx); if (defaultFilters && typeof defaultFilters.then === 'function') { // async defaultFilters defaultFilters = await defaultFilters; } } _lodash2.default.assign(filters, defaultFilters); } return filters; } /** * 分页查询 * @param {Object} [conditions] * @returns {Alaska$PaginateQuery} */ static paginate(conditions) { // $Flow let model = this; let query = model.find(conditions); let results = { total: 0, page: 1, limit: model.defaultLimit || 10, totalPage: 0, previous: 0, next: 0, results: [] }; // $Flow query.search = function (keyword) { let filters = model.createFilters(keyword); return query.where(filters); }; // $Flow query.page = function (page) { results.page = page; results.previous = page <= 1 ? 0 : page - 1; return query; }; let limitFn = query.limit; // $Flow query.limit = function (limit) { results.limit = limit; limitFn.call(query, limit); return query; }; let execFn = query.exec; // $Flow query.exec = function (callback) { // 返回 Promise return (async () => { try { // $Flow query.exec = execFn; let skip = (results.page - 1) * results.limit; // $Flow let res = await query.find().skip(skip).limit(results.limit); results.results = res; if ((res.length || skip === 0) && res.length < results.limit) { // 优化,省略count查询 results.total = skip + res.length; } else { results.total = await model.count(query.getQuery()); } results.totalPage = Math.ceil(results.total / results.limit); results.next = results.totalPage > results.page ? results.page + 1 : 0; if (callback) callback(null, results); return results; } catch (error) { if (callback) callback(error); throw error; } })(); }; // $Flow return query; } /** * 获取数据分页列表高级接口 * @param {Alaska$Context} ctx * @param {Object} [state] * @returns {mongoose.Query} */ static async paginateByContext(ctx, state) { // $Flow let model = this; let filters = await model.createFiltersByContext(ctx, state); state = Object.assign({}, ctx.state, state); let query = model.paginate(filters).page(parseInt(state.page || ctx.query._page, 10) || 1).limit(parseInt(state.limit || ctx.query._limit, 10) || model.defaultLimit || 10); const scopeKey = state.scope || ctx.query._scope || 'list'; if (scopeKey && model.autoSelect && model.scopes[scopeKey]) { //仅仅查询scope指定的字段,优化性能 query.select(model.scopes[scopeKey]); } let populations = []; _lodash2.default.forEach(model.populations, pop => { if (processPopulation(query, pop, model, scopeKey) && pop.populations) { populations.push(pop); } }); let scope = model.scopes[scopeKey] || model.defaultScope; let relationships = _lodash2.default.reduce(model.relationships, (res, r) => { if (!r.private && scope[r.key]) { res.push(r); } return res; }, []); let sort = state.sort || ctx.query._sort || model.defaultSort; if (sort) { query.sort(sort); } // Alaska$PaginateResult let results = await query; if (!results.results.length || !relationships.length && !populations.length) { return results; } //处理关联查询 let promises = []; results.results.forEach(doc => { relationships.forEach(r => { promises.push(createRelationshipQuery(r, doc, scopeKey)); }); //populations populations.forEach(pop => { _lodash2.default.forEach(pop.populations, (p, path) => { if (doc[pop.path]) { let Ref = pop.model; if (Ref && Ref.populations && Ref.populations[path]) { let record = doc[pop.path]; let config = Object.assign({}, Ref.populations[path], p); let promise = record.populate(config).execPopulate(); if (config.select) { promise.then(() => { if (record[path]) { let poplated = Array.isArray(record[path]) ? record[path] : [record[path]]; poplated.forEach(tmp => { tmp.___fields = config.select; }); } }); } promises.push(promise); } } }); }); }); await Promise.all(promises); return results; } /** * 获取数据列表高级接口 * @param {Alaska$Context} ctx * @param {Object} [state] * @returns {mongoose.Query} */ static async listByContext(ctx, state) { // $Flow let model = this; let filters = await model.createFiltersByContext(ctx, state); state = Object.assign({}, ctx.state, state); let query = model.find(filters); const scopeKey = state.scope || ctx.query._scope || 'list'; if (scopeKey && model.autoSelect && model.scopes[scopeKey]) { //仅仅查询scope指定的字段,优化性能 query.select(model.scopes[scopeKey]); } let populations = []; _lodash2.default.forEach(model.populations, pop => { if (processPopulation(query, pop, model, scopeKey) && pop.populations) { populations.push(pop); } }); let scope = model.scopes[scopeKey] || model.defaultScope; let relationships = _lodash2.default.reduce(model.relationships, (res, r) => { if (!r.private && scope[r.key]) { res.push(r); } return res; }, []); let sort = state.sort || ctx.query._sort || model.defaultSort; if (sort) { query.sort(sort); } let limit = parseInt(state.limit || ctx.query._limit) || 0; if (limit) { query.limit(limit); } let results = await query; if (!results.length || !relationships.length && !populations.length) { return results; } //处理关联查询 let promises = []; results.forEach(doc => { relationships.forEach(r => { promises.push(createRelationshipQuery(r, doc, scopeKey)); }); //populations populations.forEach(pop => { _lodash2.default.forEach(pop.populations, (p, path) => { if (doc[pop.path]) { let Ref = pop.model; if (Ref && Ref.populations && Ref.populations[path]) { let record = doc[pop.path]; let config = Object.assign({}, Ref.populations[path], p); let promise = record.populate(config).execPopulate(); if (config.select) { promise.then(() => { if (record[path]) { let poplated = Array.isArray(record[path]) ? record[path] : [record[path]]; poplated.forEach(tmp => { tmp.___fields = config.select; }); } }); } promises.push(promise); } } }); }); }); await Promise.all(promises); return results; } /** * 获取单条数据高级接口 * @param {Alaska$Context} ctx * @param {Object} [state] * @returns {mongoose.Query} */ static async showByContext(ctx, state) { // $Flow let model = this; // $Flow state = _lodash2.default.defaultsDeep({}, state, ctx.state); let id = state.id || ctx.params.id; if ((id === 'count' || id === 'paginate') && model._fields._id.type.plain !== String) { return null; } let filters = model.createFilters('', ctx.state.filters || ctx.query); // $Flow let query = model.findById(id).where(filters); let { defaultFilters } = model; if (defaultFilters) { if (typeof defaultFilters === 'function') { defaultFilters = defaultFilters(ctx); if (defaultFilters && typeof defaultFilters.then === 'function') { // async defaultFilters defaultFilters = await defaultFilters; } } query.where(defaultFilters); } const scopeKey = state.scope || ctx.query._scope || 'show'; if (model.autoSelect && model.scopes[scopeKey]) { //仅仅查询scope指定的字段,优化性能 query.select(model.scopes[scopeKey]); } let populations = []; _lodash2.default.forEach(model.populations, pop => { if (processPopulation(query, pop, model, scopeKey) && pop.populations) { populations.push(pop); } }); let scope = model.scopes[scopeKey] || model.defaultScope; let relationships = _lodash2.default.reduce(model.relationships, (res, r) => { if (!r.private && scope[r.key]) { res.push(r); } return res; }, []); // $Flow let doc = await query; if (!doc || !relationships.length && !populations.length) { return doc; } //处理关联查询 relationships let promises = relationships.map(r => createRelationshipQuery(r, doc, scopeKey)); //populations populations.forEach(pop => { _lodash2.default.forEach(pop.populations, (p, path) => { if (doc[pop.path]) { let Ref = pop.model; if (Ref && Ref.populations && Ref.populations[path]) { let record = doc[pop.path]; let config = Object.assign({}, Ref.populations[path], p); let promise = record.populate(config).execPopulate(); if (config.select) { promise.then(() => { if (record[path]) { let poplated = Array.isArray(record[path]) ? record[path] : [record[path]]; poplated.forEach(tmp => { tmp.___fields = config.select; }); } }); } promises.push(promise); } } }); }); await Promise.all(promises); return doc; } /** * 将object数据转为Model对象 * @param {Object} data * @returns {Model} */ static fromObject(data) { if (data && data.instanceOfModel) { return data; } let record = new this(null, null, true); record.init(data); return record; } /** * 将object数据转为Model对象 * @param {Array} array * @returns {Model[]} */ static fromObjectArray(array) { return array.map(data => this.fromObject(data)); } /** * 将模型数组转为plain object数组 * @param {[Model]} array * @returns {[Object]} */ static toObjectArray(array) { return _lodash2.default.map(array, record => record.toObject()); } } exports.default = Model; Model.collection = ''; Model.classOfModel = true;