Scalanie na poziomie kolumn
Scalanie na poziomie kolumn to główna przewaga merql nad naiwnym porównywaniem całych wierszy. Gdy obie strony modyfikują ten sam wiersz, merql porównuje każdą kolumnę osobno, zamiast oznaczać cały wiersz jako konflikt.
Problem ze scalaniem na poziomie wierszy
Weźmy tabelę posts, w której dwie osoby edytują ten sam wiersz:
Base: post #42 { title: "Hello", content: "Body", status: "draft" }
Ours: post #42 { title: "Hello", content: "Body v2", status: "draft" }
Theirs: post #42 { title: "New Title", content: "Body", status: "publish" }
Scalanie na poziomie wierszy wykrywa, że obie strony zmieniły wiersz, i zgłasza konflikt. Zmiany jednak wcale się nie nakładają. Nasza strona edytowała content, a druga strona — title i status.
Jak scalanie na poziomie kolumn rozwiązuje ten problem
merql stosuje te same reguły scalania trójstronnego osobno do każdej kolumny:
title: base="Hello", ours="Hello", theirs="New Title" -> accept theirs
content: base="Body", ours="Body v2", theirs="Body" -> accept ours
status: base="draft", ours="draft", theirs="publish" -> accept theirs
Wynik: { title: "New Title", content: "Body v2", status: "publish" }. Bezkonfliktowe scalanie.
Jak to działa
Gdy ThreeWayMerge wykryje, że obie strony zaktualizowały ten sam wiersz, przekazuje go do ColumnMerge::merge():
use Merql\Merge\ColumnMerge;
$result = ColumnMerge::merge(
table: 'posts',
rowKey: '42',
base: ['title' => 'Hello', 'content' => 'Body', 'status' => 'draft'],
ours: ['title' => 'Hello', 'content' => 'Body v2', 'status' => 'draft'],
theirs: ['title' => 'New Title', 'content' => 'Body', 'status' => 'publish'],
);
// $result['values'] -> merged column values
// $result['conflicts'] -> list of Conflict objects (empty if clean)
Dla każdej kolumny obowiązują te same reguły:
| Baza | Nasza strona | Druga strona | Wynik |
|---|---|---|---|
| A | A | A | Zachowaj bazę (bez zmian) |
| A | A | B | Przyjmij wartość drugiej strony |
| A | B | A | Przyjmij naszą wartość |
| A | B | B | Przyjmij (obie strony zgodne) |
| A | B | C | Konflikt (różne wartości) |
Kiedy nadal występują konflikty
Konflikt na poziomie kolumn występuje, gdy obie strony zmieniły tę samą kolumnę na różne wartości:
Base: post #42 { title: "Hello" }
Ours: post #42 { title: "Welcome" }
Theirs: post #42 { title: "Greetings" }
Obie strony zmieniły title na różne wartości. To rzeczywisty konflikt wymagający rozwiązania.
$result = ColumnMerge::merge(
'posts', '42',
['title' => 'Hello'],
['title' => 'Welcome'],
['title' => 'Greetings'],
);
// $result['conflicts'] contains one Conflict:
// table: "posts", column: "title"
// oursValue: "Welcome", theirsValue: "Greetings", baseValue: "Hello"
Częściowe konflikty
Jeden wiersz może zawierać zarówno bezkonfliktowe scalone wartości, jak i konflikty. Jeśli jedna kolumna scala się poprawnie, a inna powoduje konflikt, MergeOperation dla tego wiersza zawiera scalone wartości w kolumnach bez konfliktu, a w kolumnach powodujących konflikt — naszą wartość (domyślnie). Konflikt jest nadal zgłaszany osobno.
Base: { title: "Hello", content: "Body", status: "draft" }
Ours: { title: "Welcome", content: "Body v2", status: "draft" }
Theirs: { title: "Greetings", content: "Body", status: "publish" }
Tutaj kolumna title powoduje konflikt (obie strony zmieniły ją inaczej), content scala się bez konfliktu (zmieniła ją tylko nasza strona), a status scala się bez konfliktu (zmieniła ją tylko druga strona). Operacja zawiera:
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.
Porównywanie wartości
Wartości kolumn są porównywane jako ciągi po rzutowaniu. NULL to odrębna wartość:
NULLdoNULL= bez zmian.NULLdo"hello"= aktualizacja."hello"doNULL= aktualizacja."1"do1= bez zmian (obie wartości są rzutowane na ciąg"1").
Zgadza się to ze sposobem pobierania danych przez adapter połączenia MerQL: wszystkie wartości z bazy danych są zwracane jako ciągi (lub NULL).
Scalanie na poziomie komórek
Gdy scalanie na poziomie kolumn nadal powoduje konflikt (obie strony zmieniły tę samą kolumnę na różne wartości), merql może zejść poziom głębiej i wykonać scalanie na poziomie komórek. Działa to dla kolumn TEXT (porównywanie wiersz po wierszu) i JSON (porównywanie klucz po kluczu), rozwiązując sytuacje, w których obie strony edytowały różne części wartości tej samej kolumny.