UNPKG

7.97 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var tslib_1 = require("tslib");
4/**
5 * Orders insert or remove subjects in proper order (using topological sorting)
6 * to make sure insert or remove operations are executed in a proper order.
7 */
8var SubjectTopoligicalSorter = /** @class */ (function () {
9 // -------------------------------------------------------------------------
10 // Constructor
11 // -------------------------------------------------------------------------
12 function SubjectTopoligicalSorter(subjects) {
13 this.subjects = tslib_1.__spread(subjects); // copy subjects to prevent changing of sent array
14 this.metadatas = this.getUniqueMetadatas(this.subjects);
15 }
16 // -------------------------------------------------------------------------
17 // Public Methods
18 // -------------------------------------------------------------------------
19 /**
20 * Sorts (orders) subjects in their topological order.
21 */
22 SubjectTopoligicalSorter.prototype.sort = function (direction) {
23 var _this = this;
24 // if there are no metadatas it probably mean there is no subjects... we don't have to do anything here
25 if (!this.metadatas.length)
26 return this.subjects;
27 var sortedSubjects = [];
28 // first if we sort for deletion all junction subjects
29 // junction subjects are subjects without entity and database entity set
30 if (direction === "delete") {
31 var junctionSubjects = this.subjects.filter(function (subject) { return !subject.entity && !subject.databaseEntity; });
32 sortedSubjects.push.apply(sortedSubjects, tslib_1.__spread(junctionSubjects));
33 this.removeAlreadySorted(junctionSubjects);
34 }
35 // next we always insert entities with non-nullable relations, sort them first
36 var nonNullableDependencies = this.getNonNullableDependencies();
37 var sortedNonNullableEntityTargets = this.toposort(nonNullableDependencies);
38 if (direction === "insert")
39 sortedNonNullableEntityTargets = sortedNonNullableEntityTargets.reverse();
40 // so we have a sorted entity targets
41 // go thought each of them and find all subjects with sorted entity target
42 // add those sorted targets and remove them from original array of targets
43 sortedNonNullableEntityTargets.forEach(function (sortedEntityTarget) {
44 var entityTargetSubjects = _this.subjects.filter(function (subject) { return subject.metadata.targetName === sortedEntityTarget; });
45 sortedSubjects.push.apply(sortedSubjects, tslib_1.__spread(entityTargetSubjects));
46 _this.removeAlreadySorted(entityTargetSubjects);
47 });
48 // next sort all other entities
49 // same process as in above but with other entities
50 var otherDependencies = this.getDependencies();
51 var sortedOtherEntityTargets = this.toposort(otherDependencies);
52 if (direction === "insert")
53 sortedOtherEntityTargets = sortedOtherEntityTargets.reverse();
54 sortedOtherEntityTargets.forEach(function (sortedEntityTarget) {
55 var entityTargetSubjects = _this.subjects.filter(function (subject) { return subject.metadata.targetName === sortedEntityTarget; });
56 sortedSubjects.push.apply(sortedSubjects, tslib_1.__spread(entityTargetSubjects));
57 _this.removeAlreadySorted(entityTargetSubjects);
58 });
59 // if we have something left in the subjects add them as well
60 sortedSubjects.push.apply(sortedSubjects, tslib_1.__spread(this.subjects));
61 return sortedSubjects;
62 };
63 // -------------------------------------------------------------------------
64 // Protected Methods
65 // -------------------------------------------------------------------------
66 /**
67 * Removes already sorted subjects from this.subjects list of subjects.
68 */
69 SubjectTopoligicalSorter.prototype.removeAlreadySorted = function (subjects) {
70 var _this = this;
71 subjects.forEach(function (subject) {
72 _this.subjects.splice(_this.subjects.indexOf(subject), 1);
73 });
74 };
75 /**
76 * Extracts all unique metadatas from the given subjects.
77 */
78 SubjectTopoligicalSorter.prototype.getUniqueMetadatas = function (subjects) {
79 var metadatas = [];
80 subjects.forEach(function (subject) {
81 if (metadatas.indexOf(subject.metadata) === -1)
82 metadatas.push(subject.metadata);
83 });
84 return metadatas;
85 };
86 /**
87 * Gets dependency tree for all entity metadatas with non-nullable relations.
88 * We need to execute insertions first for entities which non-nullable relations.
89 */
90 SubjectTopoligicalSorter.prototype.getNonNullableDependencies = function () {
91 return this.metadatas.reduce(function (dependencies, metadata) {
92 metadata.relationsWithJoinColumns.forEach(function (relation) {
93 if (relation.isNullable)
94 return;
95 dependencies.push([metadata.targetName, relation.inverseEntityMetadata.targetName]);
96 });
97 return dependencies;
98 }, []);
99 };
100 /**
101 * Gets dependency tree for all entity metadatas with non-nullable relations.
102 * We need to execute insertions first for entities which non-nullable relations.
103 */
104 SubjectTopoligicalSorter.prototype.getDependencies = function () {
105 return this.metadatas.reduce(function (dependencies, metadata) {
106 metadata.relationsWithJoinColumns.forEach(function (relation) {
107 // if relation is self-referenced we skip it
108 if (relation.inverseEntityMetadata === metadata)
109 return;
110 dependencies.push([metadata.targetName, relation.inverseEntityMetadata.targetName]);
111 });
112 return dependencies;
113 }, []);
114 };
115 /**
116 * Sorts given graph using topological sorting algorithm.
117 *
118 * Algorithm is kindly taken from https://github.com/marcelklehr/toposort repository.
119 */
120 SubjectTopoligicalSorter.prototype.toposort = function (edges) {
121 function uniqueNodes(arr) {
122 var res = [];
123 for (var i_1 = 0, len = arr.length; i_1 < len; i_1++) {
124 var edge = arr[i_1];
125 if (res.indexOf(edge[0]) < 0)
126 res.push(edge[0]);
127 if (res.indexOf(edge[1]) < 0)
128 res.push(edge[1]);
129 }
130 return res;
131 }
132 var nodes = uniqueNodes(edges);
133 var cursor = nodes.length, sorted = new Array(cursor), visited = {}, i = cursor;
134 while (i--) {
135 if (!visited[i])
136 visit(nodes[i], i, []);
137 }
138 function visit(node, i, predecessors) {
139 if (predecessors.indexOf(node) >= 0) {
140 throw new Error("Cyclic dependency: " + JSON.stringify(node)); // todo: better error
141 }
142 if (!~nodes.indexOf(node)) {
143 throw new Error("Found unknown node. Make sure to provided all involved nodes. Unknown node: " + JSON.stringify(node));
144 }
145 if (visited[i])
146 return;
147 visited[i] = true;
148 // outgoing edges
149 var outgoing = edges.filter(function (edge) {
150 return edge[0] === node;
151 });
152 if (i = outgoing.length) {
153 var preds = predecessors.concat(node);
154 do {
155 var child = outgoing[--i][1];
156 visit(child, nodes.indexOf(child), preds);
157 } while (i);
158 }
159 sorted[--cursor] = node;
160 }
161 return sorted;
162 };
163 return SubjectTopoligicalSorter;
164}());
165exports.SubjectTopoligicalSorter = SubjectTopoligicalSorter;
166
167//# sourceMappingURL=SubjectTopoligicalSorter.js.map