Przejdź do treści

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:

Kod
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:

Kod
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():

PHP
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:

Kod
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.

PHP
$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.

Kod
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:

PHP
// 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ść:

  • NULL do NULL = bez zmian.
  • NULL do "hello" = aktualizacja.
  • "hello" do NULL = aktualizacja.
  • "1" do 1 = 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.