Алгоритм слияния черновиков

Общий принцип работы

В дереве Entity Groups изменения определяются относительно той ревизии из которой был создан черновик. Изменениями считаются: переименование, перемещение, создание Entity Groups (удаление тоже считается изменением, но не участвует в алгоритме слияния). Затем из изменений образуются поддеревья изменений.

Рассмотрим кейс:

Пользователь переместил subTwo и его реестры lookup1 и lookup2 внутрь группы one. Затем внутрь subTwo переместили группу subTree и ее потомка someR2 и внутри создали новый реестр someR3.

../../../_images/image0521.png

Пока пользователь работал со своей версией модели данных, актуальная версия обновилась и дерево модели данных теперь выглядит следующим образом:

../../../_images/image0551.png

При помощи diffService определяются измененные узлы дерева entity groups. При этом они оборачиваются в обертки, которые хранят расстояние до других узлов того же уровня и имя родительского узла. Отдельно хранятся расстояния до измененного и после измененного узла:

../../../_images/image0541.png
{
parent: "one",
siblingBefore: {
   rst1: 3,
   subOne: 2,
   rst2: 1
},
siblingAfter: {
   rst3: 1
}
}

Благодаря этой обертке, при слиянии дерева, узел subTwo найдет место внутри родителя one, рядом с ближайшим из его потомков.

После создания такой обертки для каждого измененного узла, формируются поддеревья. В кейсе будет сформировано одно поддерево, но если изменения будут в разных концах, то будет сформировано несколько поддеревьев и они по очереди будут вливаться в основное дерево.

Сформированное поддерево из оберток изменений:

../../../_images/image0561.png

Алгоритм начинает вливание в основное дерево, начиная с корня поддерева subTwo. Производится поиск родителя subTwo - one:

../../../_images/image0571.png

Если бы узла не было, то поддерево бы вливалось в корень Root. Далее алгоритм вливает узел subTwo.

../../../_images/image058.png

Алгоритм ищет в дереве его старую версию и удаляет ее.

../../../_images/image059.png

Затем вставляет узел subTwo внутрь своего родителя one и обходит всех потомков и анализируя какой из них ближе.

../../../_images/image060.png

Место расположения не является лучшим, потому что слева и справа нет соседей, которые указаны в обертке. Справа стоит subOne, но он должен быть левее вставляемого элемента на два узла, а в рассматриваемом случае он правее. Единственная ситуация, которая будет считаться лучше рассматриваемой, если слева будет стоять элемент, расстояние до которого один узел. Сейчас в алгоритме при равных расстояниях до соседних узлов, предпочтение отдается узлу с левой стороны.

../../../_images/image061.png

После вставки узла subTwo ему присваиваются наследуемые узлы.

../../../_images/image062.png

Затем вливается следующий элемент поддерева lookup1, как потомок subTwo. Алгоритм ищет прошлое представление узла в дереве и удаляет его.

../../../_images/image063.png

Узел lookup1 вставляется в соответствии с близостью к своим соседям. В кейсе самым близким будет lookup2 справа на два элемента. По этому lookup1 будет влит следующим образом:

../../../_images/image064.png

Если у lookup1 были бы еще узлы, то алгоритм занимался бы их вливанием. В рассматриваемом кейсе их нет, поэтому он возвращается к потомкам subTwo. Следующий в очереди потомок subTree. Алгоритм ищет в дереве предыдущий subTree и удаляет его.

../../../_images/image065.png

Алгоритм вставляет subTree, как потомка subTwo в соответствии с его соседями. Идеальная позиция, когда lookup1 расположен слева. Результат вставки:

../../../_images/image066.png

Алгоритм присваивает узлы удаленного subTree:

../../../_images/image067.png

Алгоритм начинает вливание узла subTree и начинает с someR2, где ищет его предыдущее представление для удаления.

../../../_images/image068.png

Затем алгоритм вставляет someR2, как узел subTree. В этом кейсе не будет найдено ближайших элементов (потому что у subTree отсутствуют потомки), значит someR2 будет вставлен как последний (самый правый) потомок.

../../../_images/image069.png

У someR2 нет потомков - алгоритм продолжает работу с потомками subTree и занимается вливанием someR3. Он не найдет в дереве его предыдущего представления, потому что someR3 был добавлен в черновике пользователя, поэтому никакой узел из дерева не удаляется. Узел someR3 будет вставлен, как потомок subTree. Лучшая позиция, в которой слева стоит someR2. Результат вставки:

../../../_images/image070.png

У subTree закончились его потомки и алгоритм возвращается к вливанию потомков subTwo, где узел lookup2 будет влит аналогично lookup1.

../../../_images/image071.png

Итоговый результат:

../../../_images/image072.png