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