Skip to content

Latest commit

 

History

History
787 lines (675 loc) · 13.3 KB

File metadata and controls

787 lines (675 loc) · 13.3 KB

ObjectMerger Strategy Documentation

This document describes the available merge strategies in ObjectMerger. Each section provides a description, a full example (inputs, definition, result), and a Java code snippet demonstrating usage.

Available Strategies


Standard Strategy

Strategy Name: standard
Description: Picks the first non-null value found in the source list. If all are null, returns the default value. This is the default strategy if none is specified.

Example Scenario: Merging a title field where "Legacy System" (json1) takes precedence over "New System" (json2) because json1 is first in the list.

Input Data

json1

{
  "title": "Legacy System"
}

json2

{
  "title": "New System"
}

Merge Definition

{
  "definitions": {
    "title": {
      "strategy": "standard",
      "defaultValue": "Unknown"
    }
  }
}

Result

{
  "title": "Legacy System"
}

Java Code Example

StandardFieldDefinition<String> titleDef = StandardFieldDefinition.<String>builder()
    .defaultValue("Unknown")
    .build();

Sum Strategy

Strategy Name: sum
Description: Sums up numeric values from all sources.

Example Scenario: Calculating a total score from two sources.

Input Data

json1

{
  "score": 10
}

json2

{
  "score": 20
}

Merge Definition

{
  "definitions": {
    "score": {
      "strategy": "sum",
      "defaultValue": 0
    }
  }
}

Result

{
  "score": 30
}

Java Code Example

StandardFieldDefinition<Number> scoreDef = StandardFieldDefinition.<Number>builder()
    .strategy("sum")
    .defaultValue(0)
    .build();

Concatenate Strategy

Strategy Name: concatenate
Description: Joins string values from all sources with a delimiter (default ,).

Example Scenario: Merging tags from different sources.

Input Data

json1

{
  "tags": "tag1"
}

json2

{
  "tags": "tag2"
}

Merge Definition

{
  "definitions": {
    "tags": {
      "strategy": "concatenate",
      "defaultValue": ""
    }
  }
}

Result

{
  "tags": "tag1,tag2"
}

Java Code Example

StandardFieldDefinition<String> tagsDef = StandardFieldDefinition.<String>builder()
    .strategy("concatenate")
    .defaultValue("") // or any delimiter
    .build();

Maximum Strategy

Strategy Name: maximum
Description: Selects the maximum value from all sources (Numbers or Comparables).

Example Scenario: Determining the highest access level found.

Input Data

json1

{
  "level": 5
}

json2

{
  "level": 8
}

Merge Definition

{
  "definitions": {
    "level": {
      "strategy": "maximum",
      "defaultValue": 0
    }
  }
}

Result

{
  "level": 8
}

Java Code Example

StandardFieldDefinition<Number> levelDef = StandardFieldDefinition.<Number>builder()
    .strategy("maximum")
    .defaultValue(0)
    .build();

Minimum Strategy

Strategy Name: minimum
Description: Selects the minimum value from all sources.

Example Scenario: Finding the lowest price across vendors.

Input Data

json1

{
  "price": 99.99
}

json2

{
  "price": 45.50
}

Merge Definition

{
  "definitions": {
    "price": {
      "strategy": "minimum",
      "defaultValue": 0.0
    }
  }
}

Result

{
  "price": 45.5
}

Java Code Example

StandardFieldDefinition<Number> priceDef = StandardFieldDefinition.<Number>builder()
    .strategy("minimum")
    .defaultValue(0.0)
    .build();

Average Strategy

Strategy Name: average
Description: Calculates the arithmetic mean of numeric values.

Example Scenario: Averaging ratings from different reviews.

Input Data

json1

{
  "rating": 4.0
}

json2

{
  "rating": 5.0
}

Merge Definition

{
  "definitions": {
    "rating": {
      "strategy": "average",
      "defaultValue": 0.0
    }
  }
}

Result

{
  "rating": 4.5
}

Java Code Example

StandardFieldDefinition<Number> ratingDef = StandardFieldDefinition.<Number>builder()
    .strategy("average")
    .defaultValue(0.0)
    .build();

Priority Strategy

Strategy Name: priority
Description: Selects a value based on explicit source priority ranking. Lower number indicates higher priority.

Example Scenario: Publishing status where "json2" (publisher) overrides "json1" (drafter).

Input Data

json1

{
  "status": "DRAFT"
}

json2

{
  "status": "PUBLISHED"
}

Merge Definition

{
  "definitions": {
    "status": {
      "strategy": "priority",
      "defaultValue": "UNKNOWN",
      "priority": {
        "json2": 1,
        "json1": 2
      }
    }
  }
}

Result

{
  "status": "PUBLISHED"
}

Java Code Example

Map<String, Integer> priorities = new HashMap<>();
priorities.put("json2", 1);
priorities.put("json1", 2);

PriorityFieldDefinition<String> statusDef = PriorityFieldDefinition.<String>builder()
    .strategy("priority")
    .defaultValue("UNKNOWN")
    .priority(priorities)
    .build();

Conditional Strategy (MVEL)

Strategy Name: conditional
Description: Evaluates expressions to decide which sub-strategy to use. This documentation covers usage with the MVEL Extension (objectmerger-mvel).

Context Variables: sources (List), values (Map<SourceLabel, ValueOfCurrentField>).

Example Scenario: If age is 18 (adult), prioritize json1 (where value is 'adult').

Input Data

json1

{
  "age": 18,
  "category": "adult"
}

json2

{
  "age": 18,
  "category": "minor"
}

Merge Definition

{
  "definitions": {
    "category": {
      "strategy": "conditional",
      "defaultValue": "unknown",
      "cases": [
        {
          "condition": "values['json1'] == 'adult'",
          "useStrategy": { "strategy": "priority", "priority": {"json1": 1} }
        }
      ]
    }
  }
}

Result

{
  "category": "adult"
}

Java Code Example

ConditionCase<String> adultCase = new ConditionCase<>();
adultCase.setCondition("values['json1'] == 'adult'"); // MVEL Syntax

Map<String, Integer> p = new HashMap<>();
p.put("json1", 1);
PriorityFieldDefinition<String> priorityDef = PriorityFieldDefinition.<String>builder()
    .priority(p)
    .build();

adultCase.setUseStrategy(priorityDef);

ConditionalFieldDefinition<String> categoryDef = ConditionalFieldDefinition.<String>builder()
    .defaultValue("unknown")
    .cases(List.of(adultCase))
    .build();

Conditional Strategy (GraalJS)

Strategy Name: conditional
Description: Evaluates expressions to decide which sub-strategy to use. This documentation covers usage with the GraalJS Extension (objectmerger-graaljs).

Context Variables: sources (List), values (Map<SourceLabel, ValueOfCurrentField>).

Example Scenario: If age is 18 (adult), prioritize json1 (where value is 'adult').

Input Data

json1

{
  "age": 18,
  "category": "adult"
}

json2

{
  "age": 18,
  "category": "minor"
}

Merge Definition

{
  "definitions": {
    "category": {
      "strategy": "conditional",
      "defaultValue": "unknown",
      "cases": [
        {
          "condition": "values.get('json1') == 'adult'",
          "useStrategy": { "strategy": "priority", "priority": {"json1": 1} }
        }
      ]
    }
  }
}

Result

{
  "category": "adult"
}

Java Code Example

ConditionCase<String> adultCase = new ConditionCase<>();
adultCase.setCondition("values.get('json1') == 'adult'"); // JS Syntax

// ... (Rest of setup remains the same) ...

ConditionalFieldDefinition<String> categoryDef = ConditionalFieldDefinition.<String>builder()
    .defaultValue("unknown")
    .cases(List.of(adultCase))
    .build();

MVEL Strategy

Strategy Name: mvel
Description: Executes a complex MVEL expression to calculate the value.

Context Variables: sources (Map<SourceLabel, SourceObject>).

Example Scenario: Calculating a final price by applying a discount rate from the same source.

Input Data

json1

{
  "price": 100,
  "discount": 0.1
}

Merge Definition

{
  "definitions": {
    "finalPrice": {
      "strategy": "mvel",
      "expression": "sources['json1']['price'] * (1.0 - sources['json1']['discount'])",
      "defaultValue": 0.0
    }
  }
}

Result

{
  "finalPrice": 90.0
}

Java Code Example

MvelFieldDefinition mvelDef = MvelFieldDefinition.builder()
    .expression("sources['json1']['price'] * (1.0 - sources['json1']['discount'])")
    .defaultValue(0.0)
    .build();

GraalJS Strategy

Strategy Name: graaljs
Description: Executes a complex JavaScript expression (via GraalVM Polyglot) to calculate the value.

Context Variables: sources (Map<SourceLabel, SourceObject>).

Example Scenario: Calculating a final price by applying a discount rate from the same source.

Input Data

json1

{
  "price": 100,
  "discount": 0.1
}

Merge Definition

{
  "definitions": {
    "finalPrice": {
      "strategy": "graaljs",
      "expression": "sources.get('json1').price * (1.0 - sources.get('json1').discount)",
      "defaultValue": 0.0
    }
  }
}

Result

{
  "finalPrice": 90.0
}

Java Code Example

GraalJsFieldDefinition jsDef = GraalJsFieldDefinition.builder()
    .expression("sources.get('json1').price * (1.0 - sources.get('json1').discount)")
    .defaultValue(0.0)
    .build();

List Strategy

Strategy Name: mergeList
Description: Merges list field by grouping items by an identifier field.

Example Scenario: Merging lists of items where items are identified by "id".

Input Data

json1

{
  "items": [
    { "id": "1", "name": "Item 1" }
  ]
}

json2

{
  "items": [
    { "id": "2", "name": "Item 2" }
  ]
}

Merge Definition

{
  "definitions": {
    "items": {
      "strategy": "mergeList",
      "identifyBy": "id",
      "itemMergeDefinition": {
        "definitions": {
          "name": {
            "strategy": "standard",
            "defaultValue": "Unknown"
          }
        }
      }
    }
  }
}

Result

{
  "items": [
    { "id": "1", "name": "Item 1" },
    { "id": "2", "name": "Item 2" }
  ]
}

Java Code Example

ListFieldDefinition<List<Object>> itemsDef = ListFieldDefinition.<List<Object>>builder()
    .identifyBy("id")
    .build();

ItemMergeDefinition itemDef = new ItemMergeDefinition();
// ... configure itemDef definitions ...
itemsDef.setItemMergeDefinition(itemDef);

Map Strategy

Strategy Name: mergeMap
Description: Merges map fields by union of keys.

Example Scenario: merging translation maps.

Input Data

json1

{
  "translations": {
    "en": "Hello"
  }
}

json2

{
  "translations": {
    "de": "Hallo"
  }
}

Merge Definition

{
  "definitions": {
    "translations": {
      "strategy": "mergeMap"
    }
  }
}

Result

{
  "translations": {
    "en": "Hello",
    "de": "Hallo"
  }
}

Java Code Example

MapFieldDefinition<Map<Object, Object>> transDef = MapFieldDefinition.<Map<Object, Object>>builder()
    .build();

Nested Strategy

Strategy Name: nested
Description: Recursively merges nested POJO objects based on a nested definition. This allows for granular control over sub-field merging.

Example Scenario: Merging an address where the street comes from an API source (json2) and the zip code from a database source (json1).

Input Data

json1 (db)

{
  "address": {
    "street": "Old St",
    "zip": "12345"
  }
}

json2 (api)

{
  "address": {
    "street": "New St",
    "zip": "99999"
  }
}

Merge Definition

{
  "definitions": {
    "address": {
      "strategy": "nested",
      "nestedDefinition": {
        "definitions": {
          "street": {
            "strategy": "priority",
            "priority": {"json2": 1, "json1": 2}
          },
          "zip": {
            "strategy": "priority",
            "priority": {"json1": 1, "json2": 2}
          }
        }
      }
    }
  }
}

Result

{
  "address": {
    "street": "New St",
    "zip": "12345"
  }
}

Java Code Example

NestedFieldDefinition<Address> addressDef = NestedFieldDefinition.<Address>builder()
    .nestedDefinition(nestedMergeDefinition)
    .build();