> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.ovalbee.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ovalbee.com/_mcp/server.

# Merge datasets

> Merge two OvalBee datasets into one, choosing which images to keep and how to source and resolve their annotations.

## Overview

Combines two different OvalBee datasets into one output dataset.

Merging is controlled by two independent choices. **Images** decides which images reach the output:

* **Union** keeps every image from both datasets, without duplicates.
* **From A** or **From B** keeps images from one source.
* **Intersection** keeps only images present in both datasets.
* **Unique** keeps only images present in a single dataset.

**Annotations** decides whose annotations to attach: **From Both**, **From A**, or **From B**. When you take annotations from both, **On conflict** decides what happens to an image annotated in both datasets:

* **Keep Both** attaches both annotations as separate assets. Other nodes read one annotation per image and use the newer of the two, so choose **Merge** when you want both sets of objects to count.
* **Prefer A** or **Prefer B** keeps one source's annotation.
* **Merge** combines the objects and image tags into one new annotation.
* **Skip** adds neither of the conflicting annotations.

The output holds one annotation per image, except under **Keep Both**. When the output dataset is
also one of the sources, the node replaces that dataset's own annotation with the one these rules
select rather than attaching a second annotation next to it.

The badge shows how many new images and annotations were added and opens the output dataset.

```mermaid
flowchart LR
  n1["Dataset"] --> n3["Merge datasets"]
  n2["Dataset"] --> n3
  n3["Merge datasets"] --> n4["Dataset"]
```

## Prerequisites

| Requirement                           | What you need                                                                                  |
| ------------------------------------- | ---------------------------------------------------------------------------------------------- |
| **Compatible annotations (optional)** | Annotations that convert to Supervisely format. Only needed when **On conflict** is **Merge**. |

## How it works

Choose which images to take from Dataset A and Dataset B.

Choose the annotation source and a conflict rule for images annotated in both datasets.

The node adds missing images and annotations to the output dataset, and adds the classes of both sources to its ontology.

## FAQ

#### What does Merge do when only one annotation is available?

The existing annotation is kept. A new combined annotation is created only when an image has multiple selected annotations.

#### Can I merge into an existing output dataset?

Yes, including one of the two sources. Images already there aren't added again, and an image's existing annotation is replaced by the one the rules select instead of being kept alongside it. **Merge** creates a new combined annotation on every run and detaches the one it supersedes, so rerunning the same merge leaves one annotation per image rather than piling them up.

#### When does an image count as present in both datasets?

When it's the same asset in both, for example because [Copy](/node-library/nodes/dataset-ops-copy) added it to the second dataset. The same file uploaded twice gives two separate assets, so **Union** keeps both and **Intersection** skips them.

#### Which dataset receives the result?

The dataset picked in **Output dataset**, written in place without a new version. With it empty, the node writes to its own output dataset, `Merged Dataset`. The first run creates it, and each rerun starts a new empty version of it, so earlier results stay in its history.

#### Does this node merge videos?

No. It merges image assets and their annotations. Videos in the source datasets are ignored.

## Inputs

**`Dataset A`** `dataset` — required

First dataset to merge, referred to as A in the rules below. Key: `DATASET_A`.

---

**`Dataset B`** `dataset` — required

Second dataset to merge, referred to as B in the rules below. Must differ from A. Key: `DATASET_B`.

---

**`Output dataset`** `dataset`

Where the merged result goes. Leave empty to use the node's own output dataset. Key: `OUTPUT_DATASET`.

---

**`Images`** `select` — required, default: union

Assets kept in the result - from both datasets, from one side only, from their overlap, or from their difference. Key: `ASSET_MODE`.

Options:

* Union (`union`): All images from both datasets (deduplicated).
* From A (`a`): Only images from Dataset A.
* From B (`b`): Only images from Dataset B.
* Intersection (`intersection`): Only images present in both datasets.
* Unique (`unique`): Only images present in a single dataset (in A or B, not both).

---

**`Annotations`** `select` — required, default: both

Which labels travel with the images - the ones from both datasets, or from one side only. Key: `ANNOTATION_SOURCE`.

Options:

* From Both (`both`): Take annotations from both datasets.
* From A (`a`): Take only Dataset A's annotations.
* From B (`b`): Take only Dataset B's annotations.

---

**`On conflict`** `select` — required, default: merge

What happens when both datasets have labels for the same image. Visible when `ANNOTATION_SOURCE` is `both`. Key: `ANNOTATION_CONFLICT`.

Options:

* Keep Both (`keep_both`): Attach both annotations to the image as separate assets.
* Prefer A (`prefer_a`): Use only Dataset A's annotation.
* Prefer B (`prefer_b`): Use only Dataset B's annotation.
* Merge (`merge`): Merge objects from both into a single new annotation.
* Skip (`skip`): Do not add the conflicting annotations.

---

## Outputs

**`Output Dataset`** `dataset`

ID of the dataset containing merged images and annotations. Key: `OUTPUT_DATASET`.

---

**`Merged Assets Count`** `string`

Short badge with the number of images and annotations added. Shown on the node as a badge. Selecting the badge opens `OUTPUT_DATASET`. Key: `MERGED_ASSETS_COUNT`.

---

## JSON config

Machine-readable node interface for automation and advanced usage.

#### Show JSON

```json
{
  "name": "Merge datasets",
  "description": "Merge two OvalBee datasets into one, choosing which images to keep and how to source and resolve their annotations.",
  "category": "Datasets",
  "namespace": "ovalbee",
  "templateKey": "dataset_ops/merge_datasets",
  "version": "v1",
  "inputs": [
    {
      "key": "DATASET_A",
      "label": "Dataset A",
      "type": "dataset",
      "description": "First dataset to merge, referred to as A in the rules below.",
      "required": true,
      "default": null,
      "visibleWhen": null,
      "options": {
        "creatable": false
      }
    },
    {
      "key": "DATASET_B",
      "label": "Dataset B",
      "type": "dataset",
      "description": "Second dataset to merge, referred to as B in the rules below. Must differ from A.",
      "required": true,
      "default": null,
      "visibleWhen": null,
      "options": {
        "creatable": false
      }
    },
    {
      "key": "OUTPUT_DATASET",
      "label": "Output dataset",
      "type": "dataset",
      "description": "Where the merged result goes. Leave empty to use the node's own output dataset.",
      "required": false,
      "default": null,
      "visibleWhen": null
    },
    {
      "key": "ASSET_MODE",
      "label": "Images",
      "type": "select",
      "description": "Assets kept in the result - from both datasets, from one side only, from their overlap, or from their difference.",
      "required": true,
      "default": "union",
      "visibleWhen": null,
      "options": {
        "options": [
          {
            "label": "Union",
            "value": "union",
            "tooltip": "All images from both datasets (deduplicated)."
          },
          {
            "label": "From A",
            "value": "a",
            "tooltip": "Only images from Dataset A."
          },
          {
            "label": "From B",
            "value": "b",
            "tooltip": "Only images from Dataset B."
          },
          {
            "label": "Intersection",
            "value": "intersection",
            "tooltip": "Only images present in both datasets."
          },
          {
            "label": "Unique",
            "value": "unique",
            "tooltip": "Only images present in a single dataset (in A or B, not both)."
          }
        ]
      }
    },
    {
      "key": "ANNOTATION_SOURCE",
      "label": "Annotations",
      "type": "select",
      "description": "Which labels travel with the images - the ones from both datasets, or from one side only.",
      "required": true,
      "default": "both",
      "visibleWhen": null,
      "options": {
        "options": [
          {
            "label": "From Both",
            "value": "both",
            "tooltip": "Take annotations from both datasets."
          },
          {
            "label": "From A",
            "value": "a",
            "tooltip": "Take only Dataset A's annotations."
          },
          {
            "label": "From B",
            "value": "b",
            "tooltip": "Take only Dataset B's annotations."
          }
        ]
      }
    },
    {
      "key": "ANNOTATION_CONFLICT",
      "label": "On conflict",
      "type": "select",
      "description": "What happens when both datasets have labels for the same image.",
      "required": true,
      "default": "merge",
      "visibleWhen": {
        "key": "ANNOTATION_SOURCE",
        "operator": "equals",
        "value": "both"
      },
      "options": {
        "options": [
          {
            "label": "Keep Both",
            "value": "keep_both",
            "tooltip": "Attach both annotations to the image as separate assets."
          },
          {
            "label": "Prefer A",
            "value": "prefer_a",
            "tooltip": "Use only Dataset A's annotation."
          },
          {
            "label": "Prefer B",
            "value": "prefer_b",
            "tooltip": "Use only Dataset B's annotation."
          },
          {
            "label": "Merge",
            "value": "merge",
            "tooltip": "Merge objects from both into a single new annotation."
          },
          {
            "label": "Skip",
            "value": "skip",
            "tooltip": "Do not add the conflicting annotations."
          }
        ]
      }
    }
  ],
  "outputs": [
    {
      "key": "OUTPUT_DATASET",
      "label": "Output Dataset",
      "type": "dataset",
      "description": "ID of the dataset containing merged images and annotations.",
      "artifact": false,
      "badge": false,
      "badgeOpens": null,
      "hidden": false
    },
    {
      "key": "MERGED_ASSETS_COUNT",
      "label": "Merged Assets Count",
      "type": "string",
      "description": "Short badge with the number of images and annotations added.",
      "artifact": false,
      "badge": true,
      "badgeOpens": "OUTPUT_DATASET",
      "hidden": false
    }
  ]
}
```