Как объединить 2 json-нагрузки в mule 4 с помощью dataweave

#dataweave #mulesoft #mule4

#dataweave #mulesoft #mule4

Вопрос:

Из двух разных потоков я получаю выходные данные json и то, как их объединить на основе общего ключа. Ключи являются динамическими, поэтому я хочу написать универсальный dataweave, который будет работать для любого ключа.

Ввод-1:

 [
  {
    "CustomerID": "123",
    "Name": "Maria",
    "Phone": "030-0074321"
    
  },
  {
    "CustomerID": "56654",
    "Name": "sdf",
    "Phone": "030-6877452"
    
  }
]
  

Ввод-2:

 [
    {
      "OrderID": "10643",
      "CustomerID": "123",
      "Price": "200"
    },
    {
      "OrderID": "10692",
      "CustomerID": "123",
      "Price": "566"
    },
    {
      "OrderID": "10702",
      "CustomerID": "56654",
      "Price": "546"
    }
  ]
  

Ожидаемый результат:

 [
   {
      "CustomerID":"123",
      "Name":"Maria",
      "Phone":"030-0074321",
      "Details":[
         {
            "OrderID":"10643",
            "Price":"200"
         },
         {
            "OrderID":"10692",
            "Price":"566"
         }
      ]
   },
   {
      "CustomerID":"56654",
      "Name":"sdf",
      "Phone":"030-6877452",
      "Details":[
         {
            "OrderID":"10702",
            "Price":"546"
         }
      ]
   }
]
  

На основе общего ключа (в этом примере CustomerID) Я хочу объединить оба входных данных.
Как я уже упоминал, все ключи (CustomerID, имя, телефон, OrderID, Цена) не всегда одинаковы, они динамичны.

Кто-нибудь может помочь мне написать динамический код dataweave?

Заранее спасибо

Комментарии:

1. Для этого нет формулы OOTB, но это можно сделать. Вы можете использовать рекурсивную функцию или просто функцию поиска, чтобы получить общий ключ.

Ответ №1:

Вот кое-что, что я быстро придумал:

 %dw 2.0
output application/dw

var input1 = [
  {
    "CustomerID": "123",
    "Name": "Maria",
    "Phone": "030-0074321"
    
  },
  {
    "CustomerID": "56654",
    "Name": "sdf",
    "Phone": "030-6877452"
    
  }
]

var input2 = [
    {
      "OrderID": "10643",
      "CustomerID": "123",
      "Price": "200"
    },
    {
      "OrderID": "10692",
      "CustomerID": "123",
      "Price": "566"
    },
    {
      "OrderID": "10702",
      "CustomerID": "56654",
      "Price": "546"
    }
  ]

fun combineByKey(in1,in2,k) = do {
    var groupedBy = in2 groupBy $[k]
    ---
    in1 map {
        ($),
        details: groupedBy[$[k]]
    }
}


---

combineByKey(input1,input2,"CustomerID")

//do {
//  var groupedBy = input2 groupBy $.CustomerID
//  ---
//  input1 map {
//      ($),
//      details: groupedBy[$.CustomerID]
//  }
//}
  

Как вы можете видеть из закомментированного выражения внизу, оно не такое длинное, поэтому я не думаю, что вам нужна функция IMHO.

По сути, вам нужно знать только две функции groupBy и map , затем, как создавать замыкания (иначе локализованные объявления) с помощью do {} , и, наконец, как динамически получать доступ к полям.

Бьюсь об заклад, если я потрачу немного больше времени, я смогу придумать лучшую функцию, но пока этого хватит 🙂

Потенциально есть какая-то встроенная функция, которая это уже делает, но я не знаю ни об одной. Может быть, кто-нибудь укажет на это.

Ответ №2:

Возможно, вы также можете сделать поиск общего ключа таким же динамичным. Я знаю, что это может быть излишеством, но я оставляю это здесь на случай, если это поможет каким-либо возможным способом.

 %dw 2.0
output application/json
var inp1= [
  {
    "CustomerID": "123",
    "Name": "Maria",
    "Phone": "030-0074321"
  },
  {
    "CustomerID": "56654",
    "Name": "sdf",
    "Phone": "030-6877452"
  }
]

var inp2= [
    {
      "OrderID": "10643",
      "CustomerID": "123",
      "Price": "200"
    },
    {
      "OrderID": "10692",
      "CustomerID": "123",
      "Price": "566"
    },
    {
      "OrderID": "10702",
      "CustomerID": "56654",
      "Price": "546"
    }
  ]

  var OrderIdCommon = inp1 map (firstInputValue) -> using (id = firstInputValue.OrderID) {
     (inp2 filter ($.*OrderID contains id)  map (secondInputValue) -> {
      OrderId : secondInputValue.OrderID 
    } ) 
  }

   var CustomerIdCommon = inp1 map (firstInputValue) -> using (id = firstInputValue.CustomerID) {
     (inp2 filter ($.*CustomerID contains id)  map (secondInputValue) -> {
      CustomerID: secondInputValue.CustomerID
    } ) 
  }

  var CommonKey = ((if(sizeOf(OrderIdCommon - {}) > 0) OrderIdCommon else CustomerIdCommon)[0] pluck $$)[0]

  var inp1HasCKey = inp1[0] pluck $$ contains CommonKey
  var inp2HasCKey = inp2[0] pluck $$ contains CommonKey

fun combineByKey(in1,in2,k) = do {
    var groupedBy = (in2 groupBy $[k])
    ---
    in1 map {
        ($),
        details: groupedBy[$[k]] map {
            a: (($) - k)
        }.a
    } 
}

---

(if((inp1HasCKey == true) and (inp2HasCKey == true))
{
    a: combineByKey(inp1,inp2,CommonKey as String)
}
else
{
    a: "No common key found"
}).a


  

Ответ №3:

Это преобразование dataweave соответствует критериям, которые вы упомянули.

    

    %dw 2.0
    output application/json
    ---
    input1 map(value) -> using (id = value.CustomerID)
    {
       CustomerID: value.CustomerID,
       Name: value.Name,
       Phone:value.Phone,
       Details: (input2 filter ($.*'CustomerID' contains  id) map ($ mapObject (k,v) ->{
         (v):k
         } - "CustomerID"))
    }

  

Ответ №4:

Вы можете достичь этого с помощью некоторой рекурсивной или просто множественной функции, получая ключи, затем сравнивая их между in1 и in2, а затем реструктурировать для создания foreignKey . Хитрость заключалась в создании PrimaryKey, который является значением всех общих ключей.

Попробуйте приведенный ниже скрипт. In1 подобен первичной ссылке, а in2 — вторичной ссылке. Чтобы протестировать это, попробуйте добавить Name поле в in2 и использовать хотя бы значение соответствия Name и CustomerID из in1

 %dw 2.0
var in1=[
{
  "CustomerID": "123",
  "Name": "Maria",
  "Phone": "030-0074321"
},
{
  "CustomerID": "56654",
  "Name": "sdf",
  "Phone": "030-6877452"
}
]
var in2=[
  {
    "OrderID": "10643",
    "CustomerID": "123",
    "Price": "200"
  },
  {
    "OrderID": "10692",
    "CustomerID": "123",
    "Price": "566"
  },
  {
    "OrderID": "10702",
    "CustomerID": "56654",
    "Price": "546"
  }
]
fun getCommonKey(key1, key2) = key1 -- (key1 -- key2)
var commonKey = getCommonKey(flatten(in1 map keysOf($)) distinctBy $, flatten(in2 map keysOf($)) distinctBy $)
fun getPrimaryKey(v)=(commonKey map (v1,i1) -> {
          name:v1,value: v[(v1)]
  }.value) joinBy "-"
output application/json
---
in1 map (v,i) ->{
  (v),
  (using (pkId=getPrimaryKey(v), in2WithForeignKey = in2 map (v2,i2) ->
      {
        foreignKey: (commonKey map (v1,i1) -> {
            name:v1,
            value: v2[(v1)] default ""
        }.value )  joinBy "-",
        (v2)
      }
      ){
        (using (details = ((in2WithForeignKey[?($.foreignKey == pkId)] default [])
            map (($ -- (commonKey)) - "foreignKey" ))) {
            (Details: details) if (!isEmpty(details))
      })
  })
}
  

Ответ №5:

Вот еще одно решение с общим ключом, являющимся динамическим:

 %dw 2.0
output application/json
import * from dw::core::Arrays
var inp1=[
  {
    "CustomerID": "123",
    "Name": "Maria",
    "Phone": "030-0074321"
    
  },
  {
    "CustomerID": "56654",
    "Name": "sdf",
    "Phone": "030-6877452"
    
  }
]
var inp2=[
    {
      "OrderID": "10643",
      "CustomerID": "123",
      "Price": "200"
    },
    {
      "OrderID": "10692",
      "CustomerID": "123",
      "Price": "566"
    },
    {
      "OrderID": "10702",
      "CustomerID": "56654",
      "Price": "546"
    }
  ]

//get all the keys from both the arrays
var inp1keys=((inp1 reduce(item,acc) -> item    acc) pluck $$) distinctBy $
var inp2keys=((inp2 reduce(item,acc) -> item    acc) pluck $$) distinctBy $
//get the matching key in the array
var matchingkey=((inp1keys  map (v0,k0) ->
{
    matched: if(inp2keys contains v0) v0 else null
}.matched) filter $ != null)[0]
---
/*
Steps for the script below:
1.Join both the array on the common key fetched dynamically above.
2.Remove the common key from the rigt part obtained after join.
3.After that merge the Details part for a given common key under a given common key id(achieved with the reduce)
*/
((join(inp1, inp2, (inp1) -> inp1."$(matchingkey)", (inp2) -> inp2."$(matchingkey)")) 
map (v0,k0) ->
{
    ((v0.l)    (Details:v0.r - matchingkey))
}) reduce (item,acc) ->  if( acc."$(matchingkey)" contains item."$(matchingkey)"[0])  
((acc - 'Details')    Details:[acc.Details , item.Details])
else [acc]   item
  

В любом случае, с точки зрения производительности, будьте осторожны со скриптами, подобными этим.
введите описание изображения здесь