
# Oszlopszintű egyesítés

Az oszlopszintű egyesítés a merql legfőbb előnye a naiv, soronkénti összehasonlítással szemben. Ha mindkét oldal módosítja ugyanazt a sort, a merql az egész sor ütközésként való megjelölése helyett külön-külön hasonlítja össze az oszlopokat.

## A soronkénti egyesítés problémája

Tegyük fel, hogy egy `posts` táblában két ember ugyanazt a sort szerkeszti:

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

A soronkénti egyesítés észleli, hogy mindkét oldal módosította a sort, és ütközést állapít meg. A módosítások azonban egyáltalán nem fedik egymást. Az ours a(z) `content` értékét, a theirs pedig a(z) `title` és `status` értékét módosította.

## Az ütközés feloldása oszlopszintű egyesítéssel

A merql ugyanazokat a háromutas egyesítési szabályokat alkalmazza minden egyes oszlopra:

```
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
```

Eredmény: `{ title: "New Title", content: "Body v2", status: "publish" }`. Ütközésmentes, tiszta egyesítés.

## Működés

Amikor a(z) `ThreeWayMerge` észleli, hogy mindkét oldal módosította ugyanazt a sort, átadja a feladatot a(z) `ColumnMerge::merge()` számára:

```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)
```

Minden oszlopra ugyanazok a szabályok érvényesek:

| Base | Ours | Theirs | Eredmény |
|---|---|---|---|
| A | A | A | Az alap megtartása (nincs változás) |
| A | A | B | A theirs elfogadása |
| A | B | A | Az ours elfogadása |
| A | B | B | Elfogadás (mindkét oldal egyetért) |
| A | B | C | **Ütközés** (eltérő értékek) |

## Mikor fordul elő továbbra is ütközés

Oszlopszintű ütközés akkor fordul elő, ha mindkét oldal ugyanazt az oszlopot módosítja, de eltérő értékre:

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

Mindkét oldal módosította a(z) `title` értékét, de eltérő eredményre. Ez valódi ütközés, amely feloldást igényel.

```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"
```

## Részleges ütközések

Egy soron belül lehetnek tisztán egyesíthető értékek és ütközések is. Ha az egyik oszlop tisztán egyesül, a másik pedig ütközik, a sorhoz tartozó `MergeOperation` a tisztán egyesíthető oszlopok egyesített értékeit, az ütköző oszlopoknál pedig alapértelmezés szerint az ours értékét tartalmazza. Az ütközést ettől függetlenül külön jelzi.

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

Itt a(z) `title` ütközik (mindkét oldal eltérően módosította), a(z) `content` tisztán egyesül (csak az ours módosította), a(z) `status` pedig tisztán egyesül (csak a theirs módosította). A művelet tartalmazza:

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

## Értékek összehasonlítása

Az oszlopértékeket konvertálás után karakterláncként hasonlítja össze. A `NULL` különálló értéknek számít:

- `NULL` és `NULL` között = nincs változás.
- `NULL` és `"hello"` között = módosítás.
- `"hello"` és `NULL` között = módosítás.
- `"1"` és `1` között = nincs változás (mindkettő a(z) `"1"` karakterlánccá alakul).

Ez megfelel a(z) MerQL kapcsolatadapter adatlekérési szemantikájának: az adatbázis értékei karakterláncként (vagy NULL-ként) érkeznek vissza.

## Cellaszintű egyesítés

Ha az oszlopszintű egyesítés továbbra is ütközést eredményez (mindkét oldal ugyanazt az oszlopot módosította eltérő értékre), a merql egy szinttel mélyebbre is mehet a [cellaszintű egyesítéssel](https://merql.inline0.dev/hu/docs/merge/cell-level). Ez a TEXT oszlopokra (soronkénti különbségvizsgálat) és az JSON oszlopokra (kulcsonkénti különbségvizsgálat) vonatkozik, és azokat az eseteket oldja meg, amikor mindkét oldal ugyanazon oszlopérték különböző részeit szerkesztette.
