
# Сливане на ниво колона

Сливането на ниво колона е основното предимство на merql пред наивното сравнение на ниво ред. Когато и двете страни променят един и същи ред, merql сравнява всяка колона поотделно, вместо да маркира целия ред като конфликт.

## Проблемът със сливането на ниво ред

Да разгледаме таблица `posts`, в която двама души редактират един и същи ред:

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

Сливането на ниво ред установява, че и двете страни са променили реда, и обявява конфликт. Но промените изобщо не се застъпват. Ние редактирахме `content`, а те — `title` и `status`.

## Как сливането на ниво колона разрешава проблема

merql прилага едни и същи правила за тристранно сливане към всяка колона поотделно:

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

Резултат: `{ title: "New Title", content: "Body v2", status: "publish" }`. Чисто сливане, без конфликт.

## Как работи

Когато `ThreeWayMerge` установи, че и двете страни са актуализирали един и същи ред, той предава обработката на `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)
```

За всяка колона се прилагат едни и същи правила:

| База | Нашето | Тяхното | Резултат |
|---|---|---|---|
| A | A | A | Запазва се базовата стойност (няма промяна) |
| A | A | B | Приема се тяхната стойност |
| A | B | A | Приема се нашата стойност |
| A | B | B | Приема се (и двете съвпадат) |
| A | B | C | **Конфликт** (различни стойности) |

## Кога все пак възникват конфликти

Конфликт на ниво колона възниква, когато и двете страни променят една и съща колона с различни стойности:

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

И двете страни промениха `title` с различни стойности. Това е действителен конфликт, който трябва да бъде разрешен.

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

## Частични конфликти

Един ред може да съдържа както чисто слети стойности, така и конфликти. Ако една колона се слее без конфликт, а друга породи конфликт, `MergeOperation` за реда съдържа обединените стойности в колоните без конфликт и нашата стойност (по подразбиране) в колоните с конфликт. Конфликтът се отчита отделно.

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

Тук `title` е в конфликт (и двете страни са го променили по различен начин), `content` се слива без конфликт (променили сме го само ние), а `status` се слива без конфликт (променили са го само те). Операцията съдържа:

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

## Сравнение на стойности

Стойностите в колоните се сравняват като низове след преобразуване. `NULL` е отделна стойност:

- `NULL` към `NULL` = няма промяна.
- `NULL` към `"hello"` = актуализация.
- `"hello"` към `NULL` = актуализация.
- `"1"` към `1` = няма промяна (и двете се преобразуват в низа `"1"`).

Това съответства на семантиката за извличане на адаптера за връзка MerQL: всички стойности от базата данни се връщат като низове (или NULL).

## Сливане на ниво клетка

Когато сливането на ниво колона все пак доведе до конфликт (и двете страни са променили една и съща колона с различни стойности), merql може да слезе едно ниво по-надолу с [сливане на ниво клетка](https://merql.inline0.dev/bg/docs/merge/cell-level). То се прилага към колони TEXT (разлики ред по ред) и колони JSON (разлики ключ по ключ), като разрешава случаи, в които двете страни са редактирали различни части от стойността на една и съща колона.
