#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
В любом случае, с точки зрения производительности, будьте осторожны со скриптами, подобными этим.