GUBUS REDUCE

REDUCE aggregates data across multiple objects to produce summary values. It's one of the most powerful transforms in GUBUS, enabling totals, counts, averages, grouping, and statistical analysis.

πŸ’‘

Use REDUCE to aggregate, group, and summarize data. Supports totals, counts, averages, grouping, and more. For field preservation, see the MULTISET pattern below.

Overview

REDUCE aggregates data across multiple objects to produce summary values. It’s one of the most powerful transforms in GUBUS, enabling totals, counts, averages, grouping, and statistical analysis.

When to use:

  • Calculate total revenue, sum of payments, or any numeric aggregation
  • Count items by category or status
  • Find minimum or maximum values
  • Group transactions by multiple fields (contragent, category, etc.)
  • Concatenate strings with or without uniqueness
  • Extract first/last values from ordered data

Key characteristics:

  • 3 execution modes: Simple (single value), Accum (group by field), Split (positive/negative)
  • 14+ reduce instructions covering numeric, statistical, string, and extraction operations
  • Can persist results to side objects or replace local objects
  • Supports multi-field grouping (comma-separated union_field)
  • IMPORTANT: Only keeps fields involved in reduction (union_field + from_field + to_field) if only basic property is configured. Use second config with MULTISET to preserve additional fields.

How It Works

[Read objects from reduce_source]
  β†’ [Apply reduce instruction]
      β”œβ”€ Simple Reduce: One aggregate value across all objects
      β”œβ”€ Accum Reduce: Group by union_field, aggregate each group
      └─ Split Reduce: Separate positive/negative results
  β†’ [Inherit all fields from match objects if second property with MULTISET is configured]
  β†’ [Store in new_target_to_persist or replace localObjs]
```json
{
  "reference": "groupByContragentAndKassa",
  "union_field": "contragent,kassa_type",
  "from_field": "amount",
  "instruction": "sum_of_floats",
  "to_field": "total_amount",
  "reduce_source": "local"
}
// Property 2: Preserve additional fields (SAME reference!)
{
  "reference": "groupByContragentAndKassa",  // ← SAME!
  "union_field": "contragent,kassa_type",    // ← SAME!
  "from_field": "category,subcategory,sales_quality",  // Fields to preserve
  "instruction": "multiset"
}
  "from_field": "amount",
  "to_field": "total_revenue",
  "reduce_source": "local"
}

Input: <code>[{amount: 100}, {amount: 200}, {amount: 150}]</code>

Output: <code>[{total_revenue: 450}]</code> (single object)

Use Cases:

  • Total sales across all orders
  • Count all transactions
  • Find maximum price across products
  • Average order value
πŸ’‘

Second way to achieve this result is to use GATHER_FROM_ALL_OBJECTS that will persist result in context_variables. Check out its documentation for more details.


Mode 2: Accum Reduce (Group By Field)

Service Method: executeAccumReduceWithRule

Groups by union_field, aggregates each group separately. Supports multi-field grouping (comma-separated).

Single Field Grouping:

{
  "reference": "sumByCategory",
  "instruction": "sum_of_floats",
  "union_field": "category",
  "from_field": "amount",
  "to_field": "category_total",
  "reduce_source": "local"
}

Input:

[
  { "product": "A", "category": "Electronics", "amount": 100 },
  { "product": "B", "category": "Electronics", "amount": 200 },
  { "product": "C", "category": "Furniture", "amount": 150 }
]

Output:

[
  { "category": "Electronics", "category_total": 300 },
  { "category": "Furniture", "category_total": 150 }
]

Multi-Field Grouping:

{
  "reference": "groupByContragentAndKassa",
  "instruction": "sum_of_floats",
  "union_field": "contragent,kassa_type",
  "from_field": "amount",
  "to_field": "total_amount",
  "reduce_source": "local",
  "new_target_to_persist": "side_obj",
  "new_ref_to_persist": "groupedTransactions"
}

Input:

[
  { "contragent": "ClientA", "kassa_type": "cash", "amount": 100 },
  { "contragent": "ClientA", "kassa_type": "cash", "amount": 50 },
  { "contragent": "ClientA", "kassa_type": "card", "amount": 200 },
  { "contragent": "ClientB", "kassa_type": "cash", "amount": 150 }
]

Output (in sideObjs[β€œgroupedTransactions”]):

[
  { "contragent": "ClientA", "kassa_type": "cash", "total_amount": 150 },
  { "contragent": "ClientA", "kassa_type": "card", "total_amount": 200 },
  { "contragent": "ClientB", "kassa_type": "cash", "total_amount": 150 }
]

Use Cases:

  • Group sales by region and product category
  • Sum payments by contragent and payment type
  • Count orders by status and priority
  • Average delivery time by carrier and destination

Mode 3: Split Reduce (Positive/Negative Results)

Service Methods: executeSplitReduceEquals, executeSplitReduceIncludes

Splits objects into two groups based on condition, aggregates each separately.

Split by Equals:

{
  "reference": "splitByStatus",
  "reduce_type": "count_split_equals_and_not",
  "instruction": "count",
  "from_field": "status",
  "to_field": "completed_count,pending_count",
  "instruction_args": "completed",
  "reduce_source": "local"
}

Input: <code>[{status: "completed"}, {status: "pending"}, {status: "completed"}, 
{status: "completed"}]</code>

Output: <code>[{completed_count: 3}, {pending_count: 1}]</code>

Split by Includes:

{
  "reference": "splitByRegion",
  "reduce_type": "split_reduce_includes",
  "instruction": "sum_of_ints",
  "union_field": "region",
  "from_field": "amount",
  "to_field": "target_regions_total,other_regions_total",
  "instruction_args": "East,West,South",
  "reduce_source": "local"
}

Use Cases:

  • Split revenue into target/non-target regions
  • Separate completed vs pending counts
  • Divide transactions into categories

Reduce Instructions

Numeric Aggregation

InstructionPurposefrom_fieldOutput Type
sum_of_intsSum integer valuesRequiredInteger
sum_of_floatsSum float valuesRequiredFloat
sum_of_floats_to_intSum floats, round intRequiredInteger
maxFind maximum valueRequiredNumber
minFind minimum valueRequiredNumber

Example (sum_of_floats):

{"instruction": "sum_of_floats", "from_field": "price", "to_field": "total_price"}
Input: <code>[{price: 10.5}, {price: 20.3}]</code> β†’ Output: <code>[{total_price: 30.8}]</code>

Statistical Operations

InstructionPurposefrom_fieldNotes
countCount objects with non-empty from_fieldRequiredSkips empty values

Example (count):

{"instruction": "count", "from_field": "order_id", "to_field": "order_count"}
Input: <code>[{order_id: "A"}, {order_id: ""}, {order_id: "B"}]</code> β†’ 
Output: <code>[{order_count: 2}]</code> (skips empty)

String Concatenation

InstructionPurposeUniquenessSeparator
concatJoin stringsNoNone
concat_uniqueJoin uniqueYesNone
concat_commaJoin with commaNo,
concat_unique_commaJoin unique commaYes,
concat_hyphenJoin with hyphenNo-
concat_unique_hyphenJoin unique hyphenYes-

Example (concat_unique_comma):

{"instruction": "concat_unique_comma", "from_field": "customer_name", "to_field": "all_customers"}
Input: <code>[{customer_name: "John"}, {customer_name: "Jane"}, {customer_name: "John"}]</code>

Output: <code>[{all_customers: "John, Jane"}]</code> (John appears once)

Value Extraction

InstructionPurposeBehavior
firstExtract firstTakes value from first
lastExtract lastTakes value from last
setSet single valOverwrites with each
multisetSet multipleCopies multiple fields

Example (first):

{"instruction": "first", "from_field": "created_at", "to_field": "first_created"}
Input: <code>[{created_at: "2025-01-01"}, {created_at: "2025-01-05"}]</code>
Output: <code>[{first_created: "2025-01-01"}]</code>

Example (multiset):

{"instruction": "multiset", "from_field": "name,email,phone", "to_field": "not_used"}

πŸ”‘ MULTISET Power Feature: Field Preservation in Group Reduce

MULTISET has a CRITICAL use case that solves the field discarding problem:

When using Accum Reduce (group by), you can define TWO reduce properties with the SAME reference. Both execute in the same reduce operation:

  • Property 1: Main aggregation (sum, count, max, etc.)
  • Property 2: MULTISET to preserve additional fields from first object in each group

This allows you to:

  • Group and aggregate data
  • Preserve fields that would otherwise be discarded
  • Place REDUCE anywhere in transform chain without worrying about field loss

Complete Example:

Scenario: Group transactions by contragent and kassa_type, BUT also preserve category, subcategory, and sales_quality fields that come from earlier JOIN and TRANSFORM.

Two Properties with SAME Reference:

// Property 1: Main aggregation
{
  "reference": "groupByContragentAndKassa",
  "union_field": "contragent,kassa_type",
  "from_field": "amount",
  "instruction": "sum_of_floats",
  "to_field": "total_amount",
  "reduce_source": "local"
}
// Property 2: Preserve additional fields (SAME reference!)
{
  "reference": "groupByContragentAndKassa",  // ← SAME!
  "union_field": "contragent,kassa_type",    // ← SAME!
  "from_field": "category,subcategory,sales_quality",  // Fields to preserve
  "instruction": "multiset",
}

Input (after JOIN and TRANSFORM added fields):

[
  { "contragent": "ClientA", "kassa_type": "cash", "amount": 100, "category": "Sales", 
  "subcategory": "Retail", "sales_quality": "Premium" },
  { "contragent": "ClientA", "kassa_type": "cash", "amount": 50, "category": "Sales", 
  "subcategory": "Retail", "sales_quality": "Premium" },
  { "contragent": "ClientA", "kassa_type": "card", "amount": 200, "category": "Sales", 
  "subcategory": "Online", "sales_quality": "Standard" }
]

Output:

[
  {
    "contragent": "ClientA",
    "kassa_type": "cash",
    "total_amount": 150,  // ← Aggregated by Property 1
    "category": "Sales",  // ← Preserved by Property 2 (MULTISET)
    "subcategory": "Retail",  // ← Preserved by Property 2
    "sales_quality": "Premium"  // ← Preserved by Property 2
  },
  {
    "contragent": "ClientA",
    "kassa_type": "card",
    "total_amount": 200,
    "category": "Sales",
    "subcategory": "Online",
    "sales_quality": "Standard"
  }
]
πŸ’‘

No field loss – JOIN and TRANSFORM fields are preserved. REDUCE can be placed anywhere in transform chain. Clean aggregated results with context intact.


Output Behavior & Field Retention

After reduce executes, sc.localObjs contains ONLY:

  • Fields in union_field (if Accum Reduce)
  • Field in to_field (aggregated result)
  • All other fields are DISCARDED unless preserved with MULTISET.

Common Use Cases

Use Case 1: Calculate Total Revenue

{
  "reference": "totalRevenue",
  "instruction": "sum_of_floats",
  "from_field": "amount",
  "to_field": "daily_revenue",
  "reduce_source": "local"
}
Input: <code>[{amount: 100}, {amount: 250}, {amount: 175}]</code>

Output: <code>[{daily_revenue: 525}]</code>

Use Case 2: Count By Status

{
  "reference": "countByStatus",
  "instruction": "count",
  "union_field": "status",
  "from_field": "order_id",
  "to_field": "status_count",
  "reduce_source": "local"
}
Input: <code>[{status: "completed", order_id: "A"}, {status: "pending", order_id: "B"}, 
{status: "completed", order_id: "C"}]</code>

Output: <code>[{status: "completed", status_count: 2}, {status: "pending", status_count: 1}]</code>

Use Case 3: Group Sales Summary (Multi-Field)

{
  "reference": "salesSummary",
  "instruction": "sum_of_floats",
  "union_field": "region,product_type",
  "from_field": "sales",
  "to_field": "total_sales",
  "reduce_source": "local",
  "new_target_to_persist": "side_obj",
  "new_ref_to_persist": "salesSummary",
  "message": "Sales summarized by region and product type"
}

Creates grouped summary objects in sideObjs[β€œsalesSummary”].

Use Case 4: Find Maximum Price Per Category

{
  "reference": "maxPriceByCategory",
  "instruction": "max",
  "union_field": "category",
  "from_field": "price",
  "to_field": "max_price",
  "reduce_source": "local"
}

Each category gets its maximum price.

Use Case 5: Concatenate Customer Names

{
  "reference": "allCustomers",
  "instruction": "concat_unique_comma",
  "from_field": "customer_name",
  "to_field": "customer_list",
  "reduce_source": "local"
}
Input: <code>[{customer_name: "John"}, {customer_name: "Jane"}, {customer_name: "John"}]</code>

Output: <code>[{customer_list: "John, Jane"}]</code>

Summary

REDUCE aggregates data to produce summary values:

  • 3 execution modes: Simple (single value), Accum (group by field), Split (positive/negative)
  • 14+ instructions: sum, count, max, min, concat (with variants), first, last, set, multiset
  • Multi-field grouping: Comma-separated union_field for complex categorization
  • Field discarding: Only keeps fields involved in reduction
  • Flexible output: Can persist to side objects or replace local objects

When to use: Financial calculations, statistical summaries, report generation, metrics aggregation, string concatenation, value extraction

When NOT to use: Field transformations (TRANSFORM), filtering (FILTER), merging (JOIN), data enrichment

πŸ’‘

Key for AI developers: Use upsertNonUniqueMultipleRuleProperties (with ruleName=β€œreduce”) to configure reduce rules with proper Reducer[] structure, save_all_rules to sync changes to database, and always validate with getSchemaFlow or test execution. The tool wraps upsertToAdminSheetNonUniqueMultipleRulePropsV2WOSC method and supports dual-property patterns for rules like reduce that allow multiple properties with same reference.


Next Steps

πŸ‘‰ GUBUS SCHEMA β†’ - Learn how schemas orchestrate workflows πŸ‘‰ GUBUS WORKFEED β†’ - Learn about permanent data storage πŸ‘‰ ← Back to Getting Started - Return to the main documentation