@nocobase/database 0.9.2-alpha.4 → 0.9.3-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/repository.ts CHANGED
@@ -263,7 +263,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
263
263
  * find
264
264
  * @param options
265
265
  */
266
- async find(options?: FindOptions) {
266
+ async find(options: FindOptions = {}) {
267
267
  const model = this.collection.model;
268
268
  const transaction = await this.getTransaction(options);
269
269
 
@@ -278,6 +278,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
278
278
  // @ts-ignore
279
279
  const primaryKeyField = model.primaryKeyField || model.primaryKeyAttribute;
280
280
 
281
+ // find all ids
281
282
  const ids = (
282
283
  await model.findAll({
283
284
  ...opts,
@@ -299,6 +300,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
299
300
  return [];
300
301
  }
301
302
 
303
+ // find template model
302
304
  const templateModel = await model.findOne({
303
305
  ...opts,
304
306
  includeIgnoreAttributes: false,
@@ -318,7 +320,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
318
320
  rows = await handleAppendsQuery({
319
321
  queryPromises: opts.include.map((include) => {
320
322
  const options = {
321
- ...omit(opts, ['limit', 'offset']),
323
+ ...omit(opts, ['limit', 'offset', 'filter']),
322
324
  include: include,
323
325
  where,
324
326
  transaction,
@@ -351,10 +353,9 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
351
353
  * @param options
352
354
  */
353
355
  async findAndCount(options?: FindAndCountOptions): Promise<[Model[], number]> {
354
- const transaction = await this.getTransaction(options);
355
356
  options = {
356
357
  ...options,
357
- transaction,
358
+ transaction: await this.getTransaction(options),
358
359
  };
359
360
 
360
361
  const count = await this.count(options);
@@ -472,6 +473,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
472
473
  records: options.values,
473
474
  });
474
475
  }
476
+
475
477
  const transaction = await this.getTransaction(options);
476
478
 
477
479
  const guard = UpdateGuard.fromOptions(this.model, { ...options, underscored: this.collection.options.underscored });
@@ -0,0 +1,159 @@
1
+ import { FindOptions, Repository } from '../repository';
2
+ import lodash from 'lodash';
3
+
4
+ export class AdjacencyListRepository extends Repository {
5
+ async update(options): Promise<any> {
6
+ return super.update({
7
+ ...(options || {}),
8
+ addIndex: false,
9
+ });
10
+ }
11
+
12
+ async find(options: FindOptions & { addIndex?: boolean } = {}): Promise<any> {
13
+ if (options.raw || !options.tree) {
14
+ return await super.find(options);
15
+ }
16
+
17
+ const collection = this.collection;
18
+ const primaryKey = collection.model.primaryKeyAttribute;
19
+
20
+ if (options.fields && !options.fields.includes(primaryKey)) {
21
+ options.fields.push(primaryKey);
22
+ }
23
+
24
+ const parentNodes = await super.find(options);
25
+ if (parentNodes.length === 0) {
26
+ return [];
27
+ }
28
+
29
+ const { treeParentField } = collection;
30
+ const foreignKey = treeParentField.options.foreignKey;
31
+
32
+ const childrenKey = collection.treeChildrenField?.name ?? 'children';
33
+
34
+ const parentIds = parentNodes.map((node) => node[primaryKey]);
35
+
36
+ if (parentIds.length == 0) {
37
+ this.database.logger.warn('parentIds is empty');
38
+ return parentNodes;
39
+ }
40
+
41
+ const sql = this.querySQL(parentIds, collection);
42
+
43
+ const childNodes = await this.database.sequelize.query(sql, {
44
+ type: 'SELECT',
45
+ transaction: options.transaction,
46
+ });
47
+
48
+ const childIds = childNodes.map((node) => node[primaryKey]);
49
+
50
+ const findChildrenOptions = {
51
+ ...lodash.omit(options, ['limit', 'offset', 'filterByTk']),
52
+ filter: {
53
+ [primaryKey]: childIds,
54
+ },
55
+ };
56
+
57
+ if (findChildrenOptions.fields) {
58
+ [primaryKey, foreignKey].forEach((field) => {
59
+ if (!findChildrenOptions.fields.includes(field)) {
60
+ findChildrenOptions.fields.push(field);
61
+ }
62
+ });
63
+ }
64
+
65
+ const childInstances = await super.find(findChildrenOptions);
66
+
67
+ const nodeMap = {};
68
+
69
+ childInstances.forEach((node) => {
70
+ if (!nodeMap[`${node[foreignKey]}`]) {
71
+ nodeMap[`${node[foreignKey]}`] = [];
72
+ }
73
+
74
+ nodeMap[`${node[foreignKey]}`].push(node);
75
+ });
76
+
77
+ function buildTree(parentId) {
78
+ const children = nodeMap[parentId];
79
+
80
+ if (!children) {
81
+ return [];
82
+ }
83
+
84
+ return children.map((child) => {
85
+ const childrenValues = buildTree(child.id);
86
+ if (childrenValues.length > 0) {
87
+ child.setDataValue(childrenKey, childrenValues);
88
+ }
89
+ return child;
90
+ });
91
+ }
92
+
93
+ for (const parent of parentNodes) {
94
+ const parentId = parent[primaryKey];
95
+ const children = buildTree(parentId);
96
+ if (children.length > 0) {
97
+ parent.setDataValue(childrenKey, children);
98
+ }
99
+ }
100
+
101
+ this.addIndex(parentNodes, childrenKey, options);
102
+
103
+ return parentNodes;
104
+ }
105
+
106
+ private addIndex(treeArray, childrenKey, options) {
107
+ function traverse(node, index) {
108
+ // patch for sequelize toJSON
109
+ if (node._options.includeNames && !node._options.includeNames.includes(childrenKey)) {
110
+ node._options.includeNames.push(childrenKey);
111
+ }
112
+
113
+ if (options.addIndex !== false) {
114
+ node.setDataValue('__index', `${index}`);
115
+ }
116
+
117
+ const children = node.getDataValue(childrenKey);
118
+
119
+ if (children && children.length === 0) {
120
+ node.setDataValue(childrenKey, undefined);
121
+ }
122
+
123
+ if (children && children.length > 0) {
124
+ children.forEach((child, i) => {
125
+ traverse(child, `${index}.${childrenKey}.${i}`);
126
+ });
127
+ }
128
+ }
129
+
130
+ treeArray.forEach((tree, i) => {
131
+ traverse(tree, i);
132
+ });
133
+ }
134
+
135
+ private querySQL(rootIds, collection) {
136
+ const { treeParentField } = collection;
137
+ const foreignKey = treeParentField.options.foreignKey;
138
+ const foreignKeyField = collection.model.rawAttributes[foreignKey].field;
139
+
140
+ const primaryKey = collection.model.primaryKeyAttribute;
141
+
142
+ const queryInterface = this.database.sequelize.getQueryInterface();
143
+ const q = queryInterface.quoteIdentifier.bind(queryInterface);
144
+
145
+ return `
146
+ WITH RECURSIVE cte AS (
147
+ SELECT ${q(primaryKey)}, ${q(foreignKeyField)}, 1 AS level
148
+ FROM ${collection.quotedTableName()}
149
+ WHERE ${q(foreignKeyField)} IN (${rootIds.join(',')})
150
+ UNION ALL
151
+ SELECT t.${q(primaryKey)}, t.${q(foreignKeyField)}, cte.level + 1 AS level
152
+ FROM ${collection.quotedTableName()} t
153
+ JOIN cte ON t.${q(foreignKeyField)} = cte.${q(primaryKey)}
154
+ )
155
+ SELECT ${q(primaryKey)}, ${q(foreignKeyField)} as ${q(foreignKey)}, level
156
+ FROM cte
157
+ `;
158
+ }
159
+ }