Îmbinare la nivel de coloană
Îmbinarea la nivel de coloană este principalul avantaj al merql față de comparația naivă la nivel de rând. Când ambele părți modifică același rând, merql compară fiecare coloană independent, în loc să marcheze întregul rând ca fiind în conflict.
Problema îmbinării la nivel de rând
Să presupunem că există un tabel posts în care două persoane editează același rând:
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" }
O îmbinare la nivel de rând observă că ambele părți au modificat rândul și declară un conflict. Însă modificările nu se suprapun deloc. Noi am editat content, iar ei au editat title și status.
Cum rezolvă problema îmbinarea la nivel de coloană
merql aplică aceleași reguli de îmbinare în trei direcții fiecărei coloane în mod individual:
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
Rezultat: { title: "New Title", content: "Body v2", status: "publish" }. Îmbinare fără conflicte.
Cum funcționează
Când ThreeWayMerge detectează că ambele părți au actualizat același rând, deleagă către 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)
Pentru fiecare coloană se aplică aceleași reguli:
| Bază | Ale noastre | Ale lor | Rezultat |
|---|---|---|---|
| A | A | A | Păstrează baza (fără modificări) |
| A | A | B | Acceptă valoarea lor |
| A | B | A | Acceptă valoarea noastră |
| A | B | B | Acceptă (ambele sunt de acord) |
| A | B | C | Conflict (valori diferite) |
Când apar în continuare conflicte
Un conflict la nivel de coloană apare când ambele părți au modificat aceeași coloană la valori diferite:
Base: post #42 { title: "Hello" }
Ours: post #42 { title: "Welcome" }
Theirs: post #42 { title: "Greetings" }
Ambele au modificat title la valori diferite. Acesta este un conflict real care necesită rezolvare.
$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"
Conflicte parțiale
Un singur rând poate conține atât îmbinări fără conflicte, cât și conflicte. Dacă o coloană este îmbinată fără conflicte, dar alta intră în conflict, MergeOperation pentru rând conține valorile îmbinate pentru coloanele fără conflicte și valoarea noastră (în mod implicit) pentru coloanele aflate în conflict. Conflictul este raportat separat.
Base: { title: "Hello", content: "Body", status: "draft" }
Ours: { title: "Welcome", content: "Body v2", status: "draft" }
Theirs: { title: "Greetings", content: "Body", status: "publish" }
Aici title intră în conflict (ambele părți au modificat-o diferit), content se îmbină fără conflicte (doar noi am modificat-o), iar status se îmbină fără conflicte (doar ei au modificat-o). Operația conține:
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.
Compararea valorilor
Valorile coloanelor sunt comparate ca șiruri după conversie. NULL este o valoare distinctă:
NULLlaNULL= fără modificări.NULLla"hello"= actualizare."hello"laNULL= actualizare."1"la1= fără modificări (ambele se convertesc la șirul"1").
Acest lucru corespunde semanticii de preluare a adaptorului de conexiune MerQL: toate valorile bazei de date sunt returnate ca șiruri (sau NULL).
Îmbinare la nivel de celulă
Când îmbinarea la nivel de coloană duce tot la un conflict (ambele părți au modificat aceeași coloană cu valori diferite), merql poate coborî un nivel cu îmbinarea la nivel de celulă. Aceasta se aplică coloanelor TEXT (diferențe linie cu linie) și JSON (diferențe cheie cu cheie), rezolvând cazurile în care ambele părți au editat părți diferite din aceeași valoare de coloană.