user3483129
user3483129

Reputation: 137

Aggregate data from for-each loop

Scenario - Converting a csv file to json format, taking each json element and making a get request api call. I am doing this in a for-each loop sequence. I am getting a json response (extracting eventId and cost from each). Now I wish to club all these responses together under the main header listings and make a bigger json payload.

For example:

{
   "listings": [
          {
           "eventId":"8993478",
           "cost":34
          },
          {
           "eventId":"xxxxxyyyy",
           "cost":zz
          },
   ]
}

How would I do this for all iteration entries. I can do it for a single entry(using groovy script).

Upvotes: 0

Views: 3857

Answers (3)

agentv
agentv

Reputation: 779

...I like Marco's answer and it worked perfectly for my use case.

Simply creating an array in a flow variable and using the add() method on the array in a ForEach scope did the trick.

The OP follow up question was a good one. It prompted me to do an alternate test using the approach suggested. See both of my flows here:

<flow name="sampleAggregatorFlow" doc:description="this is a simple demo that shows how to aggregate results into an accumulator array">
       <http:listener config-ref="manage-s3-api-httpListenerConfig" path="/aggregate" allowedMethods="GET" doc:name="HTTP"/>
       <set-payload value="#[['red','blue','gold']]" doc:name="Set Payload"/>
       <set-variable variableName="accumulator" value="#[[]]" doc:name="accumulator"/>
       <foreach doc:name="For Each">
          <expression-transformer expression="#[flowVars.accumulator.add(payload)]" doc:name="addEm"/>
       </foreach>
       <set-payload value="#[flowVars.accumulator]" doc:name="Set Payload"/>
    <json:object-to-json-transformer doc:name="Object to JSON"/>
</flow>
<flow name="Copy_of_sampleAggregatorFlow" doc:description="this is a simple demo that shows how to aggregate results into an accumulator array">
    <http:listener config-ref="manage-s3-api-httpListenerConfig" path="/aggregate2" allowedMethods="GET" doc:name="Copy_of_HTTP"/>
    <set-payload value="#[['red','blue','gold']]" doc:name="Copy_of_Set Payload"/>
    <set-variable variableName="accumulator" value="#[new java.util.ArrayList()]" doc:name="Copy_of_accumulator"/>
    <foreach doc:name="Copy_of_For Each">
        <expression-transformer expression="#[flowVars.accumulator.add(payload)]" doc:name="Copy_of_addEm"/>
    </foreach>
    <set-payload value="#[flowVars.accumulator]" doc:name="Copy_of_Set Payload"/>
    <json:object-to-json-transformer doc:name="Copy_of_Object to JSON"/>
</flow>

Both flows produced the same outcome:

[
  "red",
  "blue",
  "gold"
]

Tests conducted 12/26/2017 with Anypoint Studio 6.4.1 and wth Mule Runtime 3.9

Upvotes: 0

Mariano Gonzalez
Mariano Gonzalez

Reputation: 57

You can use the Batch module but you would have to rewrite this logic a little bit different. For example, you will no longer be able to use an aggregation flowVar like Marcos suggested. Instead, you would need to use a fixed size batch:commit block (which would actually be better in many ways, for example you could start sending bulks to the remote API while still processing some of the other records in the background).

Upvotes: 1

MarcosNC
MarcosNC

Reputation: 356

You could define a variable before the for-each loop as an empty list with:

<set-variable variableName="listings" value="#[[]]" />

Then, on each iteration inside the for-each loop add an element to the previous variable with:

<expression-transformer expression="#[flowVars.listings.add(flowVars.iterationMap)]" />

In the previous code fragment I used the variable flowVars.iterationMap to denote the map generated on each iteration.

Finally, if needed, you can add a set-payload transformer after the for-each loop:

<set-payload value="#[flowVars.listings]" />

HTH, Marcos

Upvotes: 4

Related Questions