Combinación a nivel de columna
La combinación a nivel de columna es la principal ventaja de merql frente a la comparación ingenua a nivel de fila. Cuando ambas partes modifican la misma fila, merql compara cada columna por separado en vez de marcar toda la fila como conflicto.
El problema de combinar a nivel de fila
Considere una tabla posts en la que dos personas editan la misma fila:
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" }
Una combinación a nivel de fila detecta que ambas partes modificaron la fila y declara un conflicto. Pero los cambios no se solapan. Nuestro lado editó content; el suyo, title y status.
Cómo lo resuelve la combinación a nivel de columna
merql aplica las mismas reglas de combinación de tres vías a cada columna por separado:
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
Resultado: { title: "New Title", content: "Body v2", status: "publish" }. Combinación limpia, sin conflictos.
Cómo funciona
Cuando ThreeWayMerge detecta que ambas partes actualizaron la misma fila, delega en 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)
Se aplican las mismas reglas a cada columna:
| Base | Nuestro lado | Su lado | Resultado |
|---|---|---|---|
| A | A | A | Conservar la base (sin cambios) |
| A | A | B | Aceptar el valor de su lado |
| A | B | A | Aceptar el valor de nuestro lado |
| A | B | B | Aceptar (ambos coinciden) |
| A | B | C | Conflicto (valores distintos) |
Cuándo siguen produciéndose conflictos
Se produce un conflicto a nivel de columna cuando ambas partes modifican la misma columna con valores distintos:
Base: post #42 { title: "Hello" }
Ours: post #42 { title: "Welcome" }
Theirs: post #42 { title: "Greetings" }
Ambas partes cambiaron title por valores distintos. Es un conflicto real que requiere resolución.
$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"
Conflictos parciales
Una misma fila puede contener combinaciones sin conflictos y conflictos. Si una columna se combina sin conflictos y otra entra en conflicto, el MergeOperation de la fila contiene los valores combinados de las columnas sin conflictos y, de forma predeterminada, el valor de nuestro lado en las columnas en conflicto. El conflicto se informa por separado.
Base: { title: "Hello", content: "Body", status: "draft" }
Ours: { title: "Welcome", content: "Body v2", status: "draft" }
Theirs: { title: "Greetings", content: "Body", status: "publish" }
Aquí title entra en conflicto (ambas partes la cambiaron de forma distinta), content se combina sin conflictos (solo cambió nuestro lado) y status se combina sin conflictos (solo cambió su lado). La operación contiene:
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.
Comparación de valores
Los valores de las columnas se comparan como cadenas después de convertirlos. NULL es un valor distinto:
NULLaNULL= sin cambios.NULLa"hello"= actualización."hello"aNULL= actualización."1"a1= sin cambios (ambos se convierten en la cadena"1").
Esto coincide con la semántica de recuperación del adaptador de conexión MerQL: todos los valores de la base de datos se devuelven como cadenas (o NULL).
Combinación a nivel de celda
Cuando la combinación a nivel de columna sigue produciendo un conflicto (ambas partes cambiaron la misma columna con valores distintos), merql puede profundizar un nivel más con la combinación a nivel de celda. Esto se aplica a columnas TEXT (diferencias línea por línea) y columnas JSON (diferencias clave por clave), y resuelve los casos en los que ambas partes editaron distintas partes del valor de una misma columna.