Ga naar de inhoud

Samenvoegen op kolomniveau

Samenvoegen op kolomniveau is het belangrijkste voordeel van merql ten opzichte van een naïeve vergelijking op rijniveau. Wanneer beide kanten dezelfde rij aanpassen, vergelijkt merql elke kolom afzonderlijk in plaats van de hele rij als conflict aan te merken.

Het probleem met samenvoegen op rijniveau

Neem een tabel posts waarin twee mensen dezelfde rij bewerken:

Code
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" }

Een samenvoeging op rijniveau ziet dat beide kanten de rij hebben gewijzigd en verklaart een conflict. Maar de wijzigingen overlappen helemaal niet. Wij hebben content bewerkt, zij hebben title en status bewerkt.

Hoe samenvoegen op kolomniveau dit oplost

merql past dezelfde driewegsamenvoegregels afzonderlijk op elke kolom toe:

Code
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

Resultaat: { title: "New Title", content: "Body v2", status: "publish" }. Schone samenvoeging, geen conflict.

Werking

Wanneer ThreeWayMerge detecteert dat beide kanten dezelfde rij hebben bijgewerkt, delegeert het aan 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)

Voor elke kolom gelden dezelfde regels:

Basis Wij Zij Resultaat
A A A Basis behouden (ongewijzigd)
A A B Hun wijziging accepteren
A B A Onze wijziging accepteren
A B B Accepteren (beide zijn het eens)
A B C Conflict (verschillende waarden)

Wanneer conflicten nog steeds optreden

Een conflict op kolomniveau treedt op wanneer beide kanten dezelfde kolom in verschillende waarden hebben veranderd:

Code
Base:    post #42  { title: "Hello" }
Ours:    post #42  { title: "Welcome" }
Theirs:  post #42  { title: "Greetings" }

Beide kanten hebben title gewijzigd in verschillende waarden. Dit is een echt conflict dat moet worden opgelost.

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"

Gedeeltelijke conflicten

Eén rij kan zowel schone samenvoegingen als conflicten bevatten. Als de ene kolom netjes wordt samengevoegd maar een andere conflicteert, bevat de MergeOperation voor de rij de samengevoegde waarden voor schone kolommen en standaard onze waarde voor conflicterende kolommen. Het conflict wordt daarnaast afzonderlijk gemeld.

Code
Base:    { title: "Hello", content: "Body",    status: "draft" }
Ours:    { title: "Welcome", content: "Body v2", status: "draft" }
Theirs:  { title: "Greetings", content: "Body",    status: "publish" }

Hier conflicteert title (beide kanten hebben deze anders gewijzigd), wordt content netjes samengevoegd (alleen wij hebben deze gewijzigd) en wordt status netjes samengevoegd (alleen zij hebben deze gewijzigd). De bewerking bevat:

PHP
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.

Waarden vergelijken

Kolomwaarden worden na conversie vergeleken als strings. NULL is een aparte waarde:

  • NULL naar NULL = geen wijziging.
  • NULL naar "hello" = bijgewerkt.
  • "hello" naar NULL = bijgewerkt.
  • "1" naar 1 = geen wijziging (beide worden geconverteerd naar de string "1").

Dit komt overeen met de ophaalsemantiek van de MerQL-verbindingsadapter: alle databasewaarden komen terug als strings (of NULL).

Samenvoegen op celniveau

Wanneer samenvoegen op kolomniveau nog steeds een conflict oplevert (beide kanten hebben dezelfde kolom in verschillende waarden gewijzigd), kan merql een niveau dieper gaan met samenvoegen op celniveau. Dit werkt voor TEXT-kolommen (verschillen regel voor regel) en JSON-kolommen (verschillen sleutel voor sleutel) en lost gevallen op waarin beide kanten verschillende delen van dezelfde kolomwaarde hebben bewerkt.