2022-12-24 08:30:01 +00:00
|
|
|
import { FindAttributeOptions, ModelStatic, Op, Sequelize } from 'sequelize';
|
2022-01-07 12:08:01 +00:00
|
|
|
import { Collection } from './collection';
|
2022-02-15 09:36:32 +00:00
|
|
|
import { Database } from './database';
|
|
|
|
import FilterParser from './filter-parser';
|
|
|
|
import { Appends, Except, FindOptions } from './repository';
|
2021-12-06 13:12:54 +00:00
|
|
|
|
|
|
|
const debug = require('debug')('noco-database');
|
|
|
|
|
2022-01-07 12:08:01 +00:00
|
|
|
interface OptionsParserContext {
|
|
|
|
collection: Collection;
|
|
|
|
targetKey?: string;
|
|
|
|
}
|
|
|
|
|
2021-12-06 13:12:54 +00:00
|
|
|
export class OptionsParser {
|
|
|
|
options: FindOptions;
|
|
|
|
database: Database;
|
2022-01-07 12:08:01 +00:00
|
|
|
collection: Collection;
|
2022-12-24 08:30:01 +00:00
|
|
|
model: ModelStatic<any>;
|
2021-12-06 13:12:54 +00:00
|
|
|
filterParser: FilterParser;
|
2022-01-07 12:08:01 +00:00
|
|
|
context: OptionsParserContext;
|
|
|
|
|
|
|
|
constructor(options: FindOptions, context: OptionsParserContext) {
|
|
|
|
const { collection } = context;
|
2021-12-06 13:12:54 +00:00
|
|
|
|
2022-01-07 12:08:01 +00:00
|
|
|
this.collection = collection;
|
|
|
|
this.model = collection.model;
|
2021-12-06 13:12:54 +00:00
|
|
|
this.options = options;
|
2022-01-07 12:08:01 +00:00
|
|
|
this.database = collection.context.database;
|
2022-04-19 09:04:54 +00:00
|
|
|
this.filterParser = new FilterParser(options?.filter, {
|
|
|
|
collection,
|
|
|
|
app: {
|
|
|
|
ctx: options?.context,
|
|
|
|
},
|
|
|
|
});
|
2022-01-07 12:08:01 +00:00
|
|
|
this.context = context;
|
2021-12-06 13:12:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isAssociation(key: string) {
|
|
|
|
return this.model.associations[key] !== undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
isAssociationPath(path: string) {
|
|
|
|
return this.isAssociation(path.split('.')[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
toSequelizeParams() {
|
2021-12-12 11:57:20 +00:00
|
|
|
const queryParams = this.filterParser.toSequelizeParams();
|
|
|
|
|
2022-01-07 12:08:01 +00:00
|
|
|
if (this.options?.filterByTk) {
|
2021-12-12 11:57:20 +00:00
|
|
|
queryParams.where = {
|
|
|
|
[Op.and]: [
|
|
|
|
queryParams.where,
|
|
|
|
{
|
2022-01-07 12:08:01 +00:00
|
|
|
[this.context.targetKey || this.collection.filterTargetKey]: this.options.filterByTk,
|
2021-12-12 11:57:20 +00:00
|
|
|
},
|
|
|
|
],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.parseSort(this.parseFields(queryParams));
|
2021-12-06 13:12:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parser sort options
|
|
|
|
* @param filterParams
|
|
|
|
* @protected
|
|
|
|
*/
|
|
|
|
protected parseSort(filterParams) {
|
2022-02-15 09:36:32 +00:00
|
|
|
let sort = this.options?.sort || [];
|
|
|
|
if (typeof sort === 'string') {
|
|
|
|
sort = sort.split(',');
|
|
|
|
}
|
2022-10-27 01:33:34 +00:00
|
|
|
|
2022-06-22 06:25:10 +00:00
|
|
|
const orderParams = [];
|
|
|
|
for (const sortKey of sort) {
|
|
|
|
let direction = sortKey.startsWith('-') ? 'DESC' : 'ASC';
|
2023-04-25 05:12:14 +00:00
|
|
|
const sortField: Array<any> = sortKey.replace('-', '').split('.');
|
2023-02-13 13:38:47 +00:00
|
|
|
|
2022-06-22 06:25:10 +00:00
|
|
|
if (this.database.inDialect('postgres', 'sqlite')) {
|
|
|
|
direction = `${direction} NULLS LAST`;
|
|
|
|
}
|
2021-12-06 13:12:54 +00:00
|
|
|
// handle sort by association
|
|
|
|
if (sortField.length > 1) {
|
|
|
|
let associationModel = this.model;
|
|
|
|
for (let i = 0; i < sortField.length - 1; i++) {
|
|
|
|
const associationKey = sortField[i];
|
|
|
|
sortField[i] = associationModel.associations[associationKey].target;
|
|
|
|
associationModel = sortField[i];
|
|
|
|
}
|
2023-02-13 13:38:47 +00:00
|
|
|
} else {
|
|
|
|
const rawField = this.model.rawAttributes[sortField[0]];
|
|
|
|
sortField[0] = rawField?.field || sortField[0];
|
2021-12-06 13:12:54 +00:00
|
|
|
}
|
2023-02-13 13:38:47 +00:00
|
|
|
|
2021-12-06 13:12:54 +00:00
|
|
|
sortField.push(direction);
|
2022-06-22 06:25:10 +00:00
|
|
|
if (this.database.inDialect('mysql')) {
|
|
|
|
orderParams.push([Sequelize.fn('ISNULL', Sequelize.col(`${this.model.name}.${sortField[0]}`))]);
|
|
|
|
}
|
|
|
|
orderParams.push(sortField);
|
|
|
|
}
|
2021-12-06 13:12:54 +00:00
|
|
|
|
|
|
|
if (orderParams.length > 0) {
|
|
|
|
return {
|
|
|
|
order: orderParams,
|
|
|
|
...filterParams,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return filterParams;
|
|
|
|
}
|
|
|
|
|
feat: database view collection (#1587)
* test: create view collection
* feat: view collection class
* feat: list view
* chore: skip sync view collection
* test: should create view collection in difference schema
* test: create view collection in collection manager
* feat: create view collection by user sql
* test: view resourcer
* feat: view collection
* feat: view collection cannot be added, deleted, or modified
* feat: view collection cannot be added, deleted, or modified
* feat: view collection cannot be added, deleted, or modified
* feat: view collection cannot be added, deleted, or modified
* refactor: connect to database view
* refactor: sync from database
* chore: rename list view sql
* chore: list view fields api
* chore: create collection without viewName
* feat: bring out fields when selecting a view
* chore: bring out fields when selecting a view
* feat: view field inference class
* chore: bring out fields when selecting a view
* chore: sync form database view
* chore: sync form database view
* refactor: view collection local
* feat: view get api
* feat: database type infer
* feat: integer map
* chore: remove from in view list
* chore: build error
* chore: uniq collection
* fix: typo
* chore: replace collection list source field
* fix: destroy view collection
* chore: timestamp field map
* refactor: interface avalableTypes
* refactor: interface avalableTypes
* chore: list fields test
* refactor: interface avalableTypes
* chore: uiSchema response in field source
* fix: view query
* chore: collection snippet
* refactor: view collection support preview
* fix: handle field source
* fix: typo
* fix: configure fileds title
* fix: configure fileds title
* fix: configure fileds title
* fix: sync from databse interface
* fix: sync from databse interface
* feat: set fields api
* fix: sync from databse fix
* feat: possibleTypes
* chore: fields get
* fix: sync from databse
* fix: list view test
* fix: view test in difference schema
* chore: comment
* feat: when there is only one source collection, the view is a subset of a Collection
* feat: view collection add field
* fix: inherit query with schema
* fix: test
* fix: ci test
* fix: test with schema
* chore: set pg default search path
* chore: mysql test
* fix: test with schema
* chore: test
* chore: action test
* chore: view column usage return type
* feat: mysql field inference
* fix: tableName
* chore: node sql parser
* fix: sql build
* fix: database build
* fix: mysql test
* feat: view collection uiSchema title
* fix: incorrect field source display when switching views
* refactor: view collection not allow modify
* fix: view collection is allow add, delete, and modify
* fix: mysql test
* fix: sqlite test
* fix: sqlite test
* fix: sqlite test
* fix: sqlite test
* chore: add id field as default target key
* style: style improve
* feat: load source field options
* style: style improve
* chore: disable remove column in view collection
* chore: support creating view collection with different schemas with the same name
* chore: support creating view collection with different schemas with the same name
* fix: query view in difference schema
* refactor: view collection viewname
* fix: query view collection in difference schema
* fix: field load
* chore: field options
* fix: mysql test
* fix: uiSchema component error when using a view field in a block
* fix: sqlite test
* chore: test
* fix: dump user views
* fix: view collection can be updated and edited in table block
* chore: sync from database display last field configuration
* chore: loadCollections
* chore: sync from database display last field configuration
* fix: field options merge issues
* style: preview table
* fix: view collection is allow using in kanban blocks
* refactor: code improve
* fix: view collection can be updated an edited in calendar block
* chore: disable infer field without interface
* feat: preview only shows source or interface fields
* fix: test
* refactor: locale
* feat: sql parser
* chore: remove node-sql-parser
* fix: yarn.lock
* test: view repository
* fix: view repository test
* chore: console.log
* chore: console.log
* fix: mysql without schema
* fix: mysql without schema
* chore: preview with field schema
* chore: tableActionInitializers
* style: preview style improve
* chore: parameter is filter when there is no filterByTk
* fix: preview pagination
* fix: preview pagination
* style: preview table style improve
* fix: sync from database loading
* chore: preview performance optimization
* chore: preview performance optimization
* feat: limit & offset
* chore: preview performance optimization
* test: field with dot column
* fix: datetime interface display
* fix: missing boolean type
* fix: sync
* fix: sync from database
* style: style improve
* style: style improve
* style: style improve
* chore: preview table
* chore: preview table
* chore: preview table
* fix: styling
---------
Co-authored-by: katherinehhh <katherine_15995@163.com>
Co-authored-by: chenos <chenlinxh@gmail.com>
2023-04-01 13:56:01 +00:00
|
|
|
protected inheritFromSubQuery(include): any {
|
|
|
|
include.push([
|
2022-11-16 04:53:58 +00:00
|
|
|
Sequelize.literal(`(select relname from pg_class where pg_class.oid = "${this.collection.name}".tableoid)`),
|
|
|
|
'__tableName',
|
feat: database view collection (#1587)
* test: create view collection
* feat: view collection class
* feat: list view
* chore: skip sync view collection
* test: should create view collection in difference schema
* test: create view collection in collection manager
* feat: create view collection by user sql
* test: view resourcer
* feat: view collection
* feat: view collection cannot be added, deleted, or modified
* feat: view collection cannot be added, deleted, or modified
* feat: view collection cannot be added, deleted, or modified
* feat: view collection cannot be added, deleted, or modified
* refactor: connect to database view
* refactor: sync from database
* chore: rename list view sql
* chore: list view fields api
* chore: create collection without viewName
* feat: bring out fields when selecting a view
* chore: bring out fields when selecting a view
* feat: view field inference class
* chore: bring out fields when selecting a view
* chore: sync form database view
* chore: sync form database view
* refactor: view collection local
* feat: view get api
* feat: database type infer
* feat: integer map
* chore: remove from in view list
* chore: build error
* chore: uniq collection
* fix: typo
* chore: replace collection list source field
* fix: destroy view collection
* chore: timestamp field map
* refactor: interface avalableTypes
* refactor: interface avalableTypes
* chore: list fields test
* refactor: interface avalableTypes
* chore: uiSchema response in field source
* fix: view query
* chore: collection snippet
* refactor: view collection support preview
* fix: handle field source
* fix: typo
* fix: configure fileds title
* fix: configure fileds title
* fix: configure fileds title
* fix: sync from databse interface
* fix: sync from databse interface
* feat: set fields api
* fix: sync from databse fix
* feat: possibleTypes
* chore: fields get
* fix: sync from databse
* fix: list view test
* fix: view test in difference schema
* chore: comment
* feat: when there is only one source collection, the view is a subset of a Collection
* feat: view collection add field
* fix: inherit query with schema
* fix: test
* fix: ci test
* fix: test with schema
* chore: set pg default search path
* chore: mysql test
* fix: test with schema
* chore: test
* chore: action test
* chore: view column usage return type
* feat: mysql field inference
* fix: tableName
* chore: node sql parser
* fix: sql build
* fix: database build
* fix: mysql test
* feat: view collection uiSchema title
* fix: incorrect field source display when switching views
* refactor: view collection not allow modify
* fix: view collection is allow add, delete, and modify
* fix: mysql test
* fix: sqlite test
* fix: sqlite test
* fix: sqlite test
* fix: sqlite test
* chore: add id field as default target key
* style: style improve
* feat: load source field options
* style: style improve
* chore: disable remove column in view collection
* chore: support creating view collection with different schemas with the same name
* chore: support creating view collection with different schemas with the same name
* fix: query view in difference schema
* refactor: view collection viewname
* fix: query view collection in difference schema
* fix: field load
* chore: field options
* fix: mysql test
* fix: uiSchema component error when using a view field in a block
* fix: sqlite test
* chore: test
* fix: dump user views
* fix: view collection can be updated and edited in table block
* chore: sync from database display last field configuration
* chore: loadCollections
* chore: sync from database display last field configuration
* fix: field options merge issues
* style: preview table
* fix: view collection is allow using in kanban blocks
* refactor: code improve
* fix: view collection can be updated an edited in calendar block
* chore: disable infer field without interface
* feat: preview only shows source or interface fields
* fix: test
* refactor: locale
* feat: sql parser
* chore: remove node-sql-parser
* fix: yarn.lock
* test: view repository
* fix: view repository test
* chore: console.log
* chore: console.log
* fix: mysql without schema
* fix: mysql without schema
* chore: preview with field schema
* chore: tableActionInitializers
* style: preview style improve
* chore: parameter is filter when there is no filterByTk
* fix: preview pagination
* fix: preview pagination
* style: preview table style improve
* fix: sync from database loading
* chore: preview performance optimization
* chore: preview performance optimization
* feat: limit & offset
* chore: preview performance optimization
* test: field with dot column
* fix: datetime interface display
* fix: missing boolean type
* fix: sync
* fix: sync from database
* style: style improve
* style: style improve
* style: style improve
* chore: preview table
* chore: preview table
* chore: preview table
* fix: styling
---------
Co-authored-by: katherinehhh <katherine_15995@163.com>
Co-authored-by: chenos <chenlinxh@gmail.com>
2023-04-01 13:56:01 +00:00
|
|
|
]);
|
|
|
|
|
|
|
|
include.push([
|
|
|
|
Sequelize.literal(`
|
|
|
|
(SELECT n.nspname
|
|
|
|
FROM pg_class c
|
|
|
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
|
|
WHERE c.oid = "${this.collection.name}".tableoid)
|
|
|
|
`),
|
|
|
|
'__schemaName',
|
|
|
|
]);
|
2022-11-16 04:53:58 +00:00
|
|
|
}
|
|
|
|
|
2021-12-06 13:12:54 +00:00
|
|
|
protected parseFields(filterParams: any) {
|
|
|
|
const appends = this.options?.appends || [];
|
|
|
|
const except = [];
|
|
|
|
|
2023-03-31 05:58:35 +00:00
|
|
|
if (this.options?.attributes) {
|
|
|
|
return {
|
|
|
|
attributes: this.options.attributes,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-12-06 13:12:54 +00:00
|
|
|
let attributes: FindAttributeOptions = {
|
|
|
|
include: [],
|
|
|
|
exclude: [],
|
|
|
|
}; // out put all fields by default
|
|
|
|
|
2022-11-16 04:53:58 +00:00
|
|
|
if (this.collection.isParent()) {
|
feat: database view collection (#1587)
* test: create view collection
* feat: view collection class
* feat: list view
* chore: skip sync view collection
* test: should create view collection in difference schema
* test: create view collection in collection manager
* feat: create view collection by user sql
* test: view resourcer
* feat: view collection
* feat: view collection cannot be added, deleted, or modified
* feat: view collection cannot be added, deleted, or modified
* feat: view collection cannot be added, deleted, or modified
* feat: view collection cannot be added, deleted, or modified
* refactor: connect to database view
* refactor: sync from database
* chore: rename list view sql
* chore: list view fields api
* chore: create collection without viewName
* feat: bring out fields when selecting a view
* chore: bring out fields when selecting a view
* feat: view field inference class
* chore: bring out fields when selecting a view
* chore: sync form database view
* chore: sync form database view
* refactor: view collection local
* feat: view get api
* feat: database type infer
* feat: integer map
* chore: remove from in view list
* chore: build error
* chore: uniq collection
* fix: typo
* chore: replace collection list source field
* fix: destroy view collection
* chore: timestamp field map
* refactor: interface avalableTypes
* refactor: interface avalableTypes
* chore: list fields test
* refactor: interface avalableTypes
* chore: uiSchema response in field source
* fix: view query
* chore: collection snippet
* refactor: view collection support preview
* fix: handle field source
* fix: typo
* fix: configure fileds title
* fix: configure fileds title
* fix: configure fileds title
* fix: sync from databse interface
* fix: sync from databse interface
* feat: set fields api
* fix: sync from databse fix
* feat: possibleTypes
* chore: fields get
* fix: sync from databse
* fix: list view test
* fix: view test in difference schema
* chore: comment
* feat: when there is only one source collection, the view is a subset of a Collection
* feat: view collection add field
* fix: inherit query with schema
* fix: test
* fix: ci test
* fix: test with schema
* chore: set pg default search path
* chore: mysql test
* fix: test with schema
* chore: test
* chore: action test
* chore: view column usage return type
* feat: mysql field inference
* fix: tableName
* chore: node sql parser
* fix: sql build
* fix: database build
* fix: mysql test
* feat: view collection uiSchema title
* fix: incorrect field source display when switching views
* refactor: view collection not allow modify
* fix: view collection is allow add, delete, and modify
* fix: mysql test
* fix: sqlite test
* fix: sqlite test
* fix: sqlite test
* fix: sqlite test
* chore: add id field as default target key
* style: style improve
* feat: load source field options
* style: style improve
* chore: disable remove column in view collection
* chore: support creating view collection with different schemas with the same name
* chore: support creating view collection with different schemas with the same name
* fix: query view in difference schema
* refactor: view collection viewname
* fix: query view collection in difference schema
* fix: field load
* chore: field options
* fix: mysql test
* fix: uiSchema component error when using a view field in a block
* fix: sqlite test
* chore: test
* fix: dump user views
* fix: view collection can be updated and edited in table block
* chore: sync from database display last field configuration
* chore: loadCollections
* chore: sync from database display last field configuration
* fix: field options merge issues
* style: preview table
* fix: view collection is allow using in kanban blocks
* refactor: code improve
* fix: view collection can be updated an edited in calendar block
* chore: disable infer field without interface
* feat: preview only shows source or interface fields
* fix: test
* refactor: locale
* feat: sql parser
* chore: remove node-sql-parser
* fix: yarn.lock
* test: view repository
* fix: view repository test
* chore: console.log
* chore: console.log
* fix: mysql without schema
* fix: mysql without schema
* chore: preview with field schema
* chore: tableActionInitializers
* style: preview style improve
* chore: parameter is filter when there is no filterByTk
* fix: preview pagination
* fix: preview pagination
* style: preview table style improve
* fix: sync from database loading
* chore: preview performance optimization
* chore: preview performance optimization
* feat: limit & offset
* chore: preview performance optimization
* test: field with dot column
* fix: datetime interface display
* fix: missing boolean type
* fix: sync
* fix: sync from database
* style: style improve
* style: style improve
* style: style improve
* chore: preview table
* chore: preview table
* chore: preview table
* fix: styling
---------
Co-authored-by: katherinehhh <katherine_15995@163.com>
Co-authored-by: chenos <chenlinxh@gmail.com>
2023-04-01 13:56:01 +00:00
|
|
|
this.inheritFromSubQuery(attributes.include);
|
2022-11-16 04:53:58 +00:00
|
|
|
}
|
|
|
|
|
2021-12-06 13:12:54 +00:00
|
|
|
if (this.options?.fields) {
|
2023-04-16 09:30:39 +00:00
|
|
|
attributes = [];
|
|
|
|
if (this.collection.isParent()) {
|
|
|
|
this.inheritFromSubQuery(attributes);
|
|
|
|
}
|
|
|
|
|
2021-12-06 13:12:54 +00:00
|
|
|
// 将fields拆分为 attributes 和 appends
|
|
|
|
for (const field of this.options.fields) {
|
|
|
|
if (this.isAssociationPath(field)) {
|
|
|
|
// field is association field
|
|
|
|
appends.push(field);
|
|
|
|
} else {
|
|
|
|
// field is model attribute, change attributes to array type
|
|
|
|
attributes.push(field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options?.except) {
|
|
|
|
for (const exceptKey of this.options.except) {
|
|
|
|
if (this.isAssociationPath(exceptKey)) {
|
|
|
|
// except association field
|
|
|
|
except.push(exceptKey);
|
|
|
|
} else {
|
|
|
|
// if attributes is array form, ignore except
|
|
|
|
if (Array.isArray(attributes)) continue;
|
|
|
|
attributes.exclude.push(exceptKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
attributes,
|
|
|
|
...this.parseExcept(except, this.parseAppends(appends, filterParams)),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
protected parseExcept(except: Except, filterParams: any) {
|
|
|
|
if (!except) return filterParams;
|
|
|
|
const setExcept = (queryParams: any, except: string) => {
|
|
|
|
// split exceptKey to path form
|
|
|
|
// posts.comments.content => ['posts', 'comments', 'content']
|
|
|
|
// then set except on include attributes
|
|
|
|
const exceptPath = except.split('.');
|
|
|
|
const association = exceptPath[0];
|
|
|
|
const lastLevel = exceptPath.length <= 2;
|
|
|
|
|
2023-04-25 05:12:14 +00:00
|
|
|
const existIncludeIndex = queryParams['include'].findIndex((include) => include['association'] == association);
|
2021-12-06 13:12:54 +00:00
|
|
|
|
|
|
|
if (existIncludeIndex == -1) {
|
|
|
|
// if include not exists, ignore this except
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastLevel) {
|
|
|
|
// if it not have exclude form
|
2021-12-06 13:23:34 +00:00
|
|
|
if (Array.isArray(queryParams['include'][existIncludeIndex]['attributes'])) {
|
2021-12-06 13:12:54 +00:00
|
|
|
return;
|
|
|
|
} else {
|
2021-12-06 13:23:34 +00:00
|
|
|
if (!queryParams['include'][existIncludeIndex]['attributes']['exclude']) {
|
|
|
|
queryParams['include'][existIncludeIndex]['attributes']['exclude'] = [];
|
2021-12-06 13:12:54 +00:00
|
|
|
}
|
|
|
|
|
2021-12-06 13:23:34 +00:00
|
|
|
queryParams['include'][existIncludeIndex]['attributes']['exclude'].push(exceptPath[1]);
|
2021-12-06 13:12:54 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-12-06 13:23:34 +00:00
|
|
|
setExcept(queryParams['include'][existIncludeIndex], exceptPath.filter((_, index) => index !== 0).join('.'));
|
2021-12-06 13:12:54 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const exceptKey of except) {
|
|
|
|
setExcept(filterParams, exceptKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
return filterParams;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected parseAppends(appends: Appends, filterParams: any) {
|
|
|
|
if (!appends) return filterParams;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set include params
|
2022-10-16 11:16:14 +00:00
|
|
|
* @param model
|
|
|
|
* @param queryParams
|
|
|
|
* @param append
|
2021-12-06 13:12:54 +00:00
|
|
|
*/
|
2022-12-24 08:30:01 +00:00
|
|
|
const setInclude = (model: ModelStatic<any>, queryParams: any, append: string) => {
|
2021-12-06 13:12:54 +00:00
|
|
|
const appendFields = append.split('.');
|
|
|
|
const appendAssociation = appendFields[0];
|
|
|
|
|
2022-02-17 09:41:30 +00:00
|
|
|
const associations = model.associations;
|
|
|
|
|
2021-12-06 13:12:54 +00:00
|
|
|
// if append length less or equal 2
|
|
|
|
// example:
|
|
|
|
// appends: ['posts']
|
|
|
|
// appends: ['posts.title']
|
|
|
|
// All of these can be seen as last level
|
2023-04-25 05:12:14 +00:00
|
|
|
let lastLevel = false;
|
2022-02-17 04:56:52 +00:00
|
|
|
|
|
|
|
if (appendFields.length == 1) {
|
|
|
|
lastLevel = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (appendFields.length == 2) {
|
2023-05-11 04:47:31 +00:00
|
|
|
const association = associations[appendFields[0]];
|
|
|
|
if (!association) {
|
|
|
|
throw new Error(`association ${appendFields[0]} in ${model.name} not found`);
|
|
|
|
}
|
|
|
|
|
2022-02-17 04:56:52 +00:00
|
|
|
const associationModel = associations[appendFields[0]].target;
|
|
|
|
if (associationModel.rawAttributes[appendFields[1]]) {
|
|
|
|
lastLevel = true;
|
|
|
|
}
|
|
|
|
}
|
2021-12-06 13:12:54 +00:00
|
|
|
|
|
|
|
// find association index
|
|
|
|
if (queryParams['include'] == undefined) {
|
|
|
|
queryParams['include'] = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
let existIncludeIndex = queryParams['include'].findIndex(
|
|
|
|
(include) => include['association'] == appendAssociation,
|
|
|
|
);
|
|
|
|
|
|
|
|
// if association not exist, create it
|
|
|
|
if (existIncludeIndex == -1) {
|
|
|
|
// association not exists
|
|
|
|
queryParams['include'].push({
|
|
|
|
association: appendAssociation,
|
|
|
|
});
|
|
|
|
|
2022-05-10 16:25:43 +00:00
|
|
|
existIncludeIndex = queryParams['include'].length - 1;
|
2021-12-06 13:12:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// end appends
|
|
|
|
// without nests association
|
|
|
|
if (lastLevel) {
|
|
|
|
// get exist association attributes
|
2021-12-06 13:23:34 +00:00
|
|
|
let attributes = queryParams['include'][existIncludeIndex]['attributes'] || {
|
2021-12-06 13:12:54 +00:00
|
|
|
include: [], // all fields are output by default
|
|
|
|
};
|
|
|
|
|
|
|
|
// if need set attribute
|
|
|
|
if (appendFields.length == 2) {
|
|
|
|
if (!Array.isArray(attributes)) {
|
|
|
|
attributes = [];
|
|
|
|
}
|
|
|
|
|
2022-02-17 04:56:52 +00:00
|
|
|
const attributeName = appendFields[1];
|
|
|
|
|
2021-12-06 13:12:54 +00:00
|
|
|
// push field to it
|
2022-02-17 04:56:52 +00:00
|
|
|
attributes.push(attributeName);
|
2021-12-06 13:12:54 +00:00
|
|
|
} else {
|
|
|
|
// if attributes is empty array, change it to object
|
|
|
|
if (Array.isArray(attributes) && attributes.length == 0) {
|
|
|
|
attributes = {
|
|
|
|
include: [],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set new attributes
|
|
|
|
queryParams['include'][existIncludeIndex] = {
|
|
|
|
...queryParams['include'][existIncludeIndex],
|
|
|
|
attributes,
|
|
|
|
};
|
|
|
|
} else {
|
2023-05-08 03:18:42 +00:00
|
|
|
const existInclude = queryParams['include'][existIncludeIndex];
|
|
|
|
if (existInclude.attributes && Array.isArray(existInclude.attributes) && existInclude.attributes.length == 0) {
|
|
|
|
existInclude.attributes = {
|
|
|
|
include: [],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-02-17 09:41:30 +00:00
|
|
|
setInclude(
|
|
|
|
model.associations[queryParams['include'][existIncludeIndex].association].target,
|
|
|
|
queryParams['include'][existIncludeIndex],
|
|
|
|
appendFields.filter((_, index) => index !== 0).join('.'),
|
|
|
|
);
|
2021-12-06 13:12:54 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// handle every appends
|
|
|
|
for (const append of appends) {
|
2022-02-17 09:41:30 +00:00
|
|
|
setInclude(this.model, filterParams, append);
|
2021-12-06 13:12:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
debug('filter params: %o', filterParams);
|
|
|
|
return filterParams;
|
|
|
|
}
|
|
|
|
}
|