Search code examples
muledataweavemule4

How to Java code to generate a string from array to DataWeave 2?


Am working on migrating a Mule 3 application to Mule 4. We are using Java code to generate an Oracle database query Here is the existing Java function:

public static String generateStringInClause(String tableAlias, String fieldName, List<String> keys) {
    String inClause = "";
    for (int i = 0; i < keys.size(); i++){
        
        if (i==0) {
            inClause += "('" + keys.get(i) + "'";
        } else if ((i+1)%10==0) {
            inClause += ",'" + keys.get(i) + "')";
        } else if (i%10==0) {
            inClause += " or "+tableAlias+"."+fieldName+" in ('" + keys.get(i) + "'";
        } else if (i == keys.size()-1){
            inClause += ",'" + keys.get(i) + "')";
        } else {
            inClause += ",'" + keys.get(i) + "'";
        }
    }
    if (keys.size() % 10 == 1 || keys.size() == 1) {
        inClause = inClause + ")";
    }       
    return inClause;
}  

Here are the actual outputs from the Java code when I pass a List of keys of different sizes:

      * 21 = ('a0','a1','a2'...'a8','a9') or xyz.abc in ('a10','a11',...'a19') or xyz.abc in ('a20')
      * 12 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9') or xyz.abc in ('a10','a11')
      * 11 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9') or xyz.abc in ('a10')
      *      
      * 10 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9')
      * 09 = ('a0','a1','a2','a3','a4','a5','a6','a7','a8')
      * 01 = ('a0')
      

Now In Mule 4 we do not want to use any Java methods / classes so need to convert this function into DataWeave

Here is what I have tried:

%dw 2.0
output application/java
var inClause = ""
var size = sizeOf(payload.keys)
fun test() = payload.keys map ((item, index) -> 
    if (index ==0) ( inClause ++ "('" ++ item ++ "'") 
    else if (mod((index+1),10)==0) ( "'" ++ item ++ "')")
    else if (mod((index),10)==0) ( " or "++ payload.tableAlias ++ "." ++ payload.fieldName ++ " in ('" ++ item ++ "'") 
    else if (index == (size-1) ) ( "'" ++ item ++ "')")
    else ("'" ++ item ++ "'")
)
var result = test() joinBy ","
var result1 = if((mod(size,10) == 1) or (size == 1)) (result ++ ")") else (result)
---
result1

This script works fine and generates the same result when I have a list up to 10 elements. It does not produce the same result as the Java method when I have > 10 elements in the list of keys.

Input to the DataWeave script:

{
    "tableAlias": "xyz",
    "fieldName": "abc",
    "keys" : ["a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","a10"]    
}

In the output there is an additional comma , before or which will cause SQL query to fail.

Actual output:

('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9'), or xyz.abc in ('a10')

Solution

  • You are trying to migrate the logic from the Java method but you need to take into account that Java is an imperative language and DataWeave a functional language. A 1 to 1 migration may not work or be too difficult to maintain. For example the variable inClause does absolutely nothing in the DataWeave script. You can not accumulate a value over a loop like in Java. You should think of what you are trying to achieve as the output and then think on how to express that.

    As an example, I choose first separate the keys into blocks of n. I only need a condition to identify the first block. I don't need to transform each value after I know if it is the first block or one of the rest. That way I don't need to concern myself with matching commas or parenthesis. Then I transform each block more naturally in my view. I added an auxiliary function to format the values in each block. I use a reduce() at the end to concatenate the string resulting of each block. The intention of the code should be more clear than in the Java code.

    I encapsulated the inputs to the functions in parameters so it is more reusable and clean. The block size is also a parameter. Magic numbers in code are not a good practice.

    %dw 2.0
    output application/java
    import * from dw::core::Arrays
    
    fun quoteArray(a) = "(" ++ (a map ("'" ++ $ ++ "'") joinBy  ",") ++ ")"
    
    fun generateStringInClause(tableAlias, fieldName, keys, size) = 
        keys divideBy size 
            map ((item, index) -> 
                if (index == 0) quoteArray(item)
                else (" or "++ tableAlias ++ "." ++ fieldName ++ " in " ++ quoteArray(item) )
            )    
        reduce ((item, accumulator="") -> accumulator ++ item)
    ---
    generateStringInClause(payload.tableAlias, payload.fieldName, payload.keys, 10)
    

    Input:

      {
        "tableAlias": "xyz",
        "fieldName": "abc",
        "keys" : ["a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","a10","a11", "a12","a13","a14","a15","a16","a17","a18","a19","a20"]    
    }
    

    Output:

    ('a0','a1','a2','a3','a4','a5','a6','a7','a8','a9') or xyz.abc in ('a10','a11','a12','a13','a14','a15','a16','a17','a18','a19') or xyz.abc in ('a20')