Groovy 用于追加 xml 节点的脚本代码需要 15 多个小时来处理 6000 条员工记录
Groovy script code to append xml node taking 15+ hours for 6K employee records
下面的代码可以正常工作,但执行 6000 条员工记录需要 15 个多小时,是否可以进行任何改进?
我有 6000 名员工中的每一个的两个员工记录结构(员工数据和员工福利)我使用人员编号将它们合并为单个 xml(要检查 xml 结构,请检查我的上一个问题 - ).
现在,当 ID(multimap:Message1 中的 personIdExternal 在 multimap:Message2 中找到相同的 ID / PERNR 时,我必须在 xml 员工记录中附加一个 node/subnode。
xml.'**'.findAll{it.name() == 'EmpEmployment'}.each{ p->
def perID = xml.'**'.find{it.personIdExternal.text() == p.personIdExternal.text()}
def pernr = xml.'**'.find{it.PERNR.text() == '000'+perID.personIdExternal.text()}
if(pernr != null)
{
perID.appendNode {
erpBenEligibility(pernr.PARDT.text()) }
}
}
message.setBody(groovy.xml.XmlUtil.serialize(xml))
样本XML:
<?xml version='1.0' encoding='UTF-8'?>
<multimap:Messages xmlns:multimap="http://sap.com/xi/XI/SplitAndMerge">
<multimap:Message1>
<person>
<person>
<street>test_stree1</street>
<city>test_city1</city>
<state>test_state1</state>
<EmpEmployment>
<personIdExternal> 001 </personIdExternal>
</EmpEmployment>
</person>
<person>
<street>test_stree2</street>
<city>test_city2</city>
<state>test_state2</state>
<EmpEmployment>
<personIdExternal> 002 </personIdExternal>
</EmpEmployment>
</person>
<person>
<street>test_stree3</street>
<city>test_city3</city>
<state>test_state3</state>
<EmpEmployment>
<personIdExternal> 003</personIdExternal>
</EmpEmployment>
</person>
</person>
</multimap:Message1>
<multimap:Message2>
<rfc:ZHR_GET_EMP_BENEFIT_DETAILS.Response xmlns:rfc="urn:sap-
com:document:sap:rfc:functions">
<phone>
<home>
<phone>number1</phone>
</home>
<PERNR> 001 </PERNR>
<PARDT>#### 1 ####</PARDT>
<home>
<phone>number2</phone>
</home>
<PERNR> 002 </PERNR>
<PARDT>#### 2 ####</PARDT>
<home>
<phone>number3</phone>
</home>
<PERNR> 003 </PERNR>
<PARDT>#### 3 ####</PARDT>
</phone>
</rfc:ZHR_GET_EMP_BENEFIT_DETAILS.Response xmlns:rfc="urn:sap-com:document:sap:rfc:functions">
</multimap:Message2>
</multimap:Messages>
您的代码中的一些主要问题:
- 使用
.**
访问器。如果 message1 中有 10000 persons
,那么 xml.**
将 return 一个包含 count(person)+count(EmpEmployment)+count(personIdExternal) = 10000*3
个元素的数组。并在此数组上调用 findAll 应该扫描所有这些元素
- 在主循环内
xml.'**'.findAll{it.name() == 'EmpEmployment'}.each{
您无缘无故地构建了嵌套的大型数组。例如在这个表达式 def perID = xml.'**'.find{it.personIdExternal.text() == p.personIdExternal.text()}
之后你有 perID
等于 p
您的代码仍然不符合 xml 示例。
所以,我将做一些假设来展示如何在没有 .**.
:
的情况下构建 gpath
让我们 xml 像这样:
<?xml version='1.0' encoding='UTF-8'?>
<multimap:Messages xmlns:multimap="http://sap.com/xi/XI/SplitAndMerge">
<multimap:Message1>
<person>
<person>
<EmpEmployment>
<personIdExternal>001</personIdExternal>
</EmpEmployment>
</person>
</person>
</multimap:Message1>
<multimap:Message2>
<phone>
<xyz>
<PERNR>000001</PERNR>
<PARDT>#### 1 ####</PARDT>
</xyz>
</phone>
</multimap:Message2>
</multimap:Messages>
这是构建大型 xml 消息的代码部分:
def count = 60000 //just for test let's create xml with 60K elements
def msg = '''<?xml version='1.0' encoding='UTF-8'?>
<multimap:Messages xmlns:multimap="http://sap.com/xi/XI/SplitAndMerge">
<multimap:Message1>
<person>
'''+
(1..count).collect{"""\
<person>
<EmpEmployment>
<personIdExternal>${String.format('%03d',it)}</personIdExternal>
</EmpEmployment>
</person>
"""}.join()+
''' </person>
</multimap:Message1>
<multimap:Message2>
<phone>
'''+
(1..count).collect{"""\
<xyz>
<PERNR>${String.format('%06d',it)}</PERNR>
<PARDT>#### ${it} ####</PARDT>
</xyz>
"""}.join()+
''' </phone>
</multimap:Message2>
</multimap:Messages>
'''
现在修改后的转换算法:
def xml = new XmlParser().parseText(msg)
def t = System.currentTimeMillis()
def ns = new groovy.xml.Namespace('http://sap.com/xi/XI/SplitAndMerge')
//for fast search let map PERNR value to a node that contains it
def pernrMap=xml[ns.Message2][0].phone[0].children().collectEntries{ [it.PERNR.text(), it] }
//itearte msg1 -> find entry in pernrMap -> add node
xml[ns.Message1][0].person[0].person.each{p->
def emp = p.EmpEmployment[0]
def pernr = pernrMap['000'+emp.personIdExternal.text()]
if(pernr) emp.appendNode('erpBenEligibility', null, pernr.PARDT.text() )
}
groovy.xml.XmlUtil.serialize(xml)
println "t = ${(System.currentTimeMillis()-t)/1000} sec"
即使对于 msg1 和 msg2 中的 60k 个元素,它也会在不到 1 秒的时间内完成转换。
下面的代码可以正常工作,但执行 6000 条员工记录需要 15 个多小时,是否可以进行任何改进?
我有 6000 名员工中的每一个的两个员工记录结构(员工数据和员工福利)我使用人员编号将它们合并为单个 xml(要检查 xml 结构,请检查我的上一个问题 - ).
现在,当 ID(multimap:Message1 中的 personIdExternal 在 multimap:Message2 中找到相同的 ID / PERNR 时,我必须在 xml 员工记录中附加一个 node/subnode。
xml.'**'.findAll{it.name() == 'EmpEmployment'}.each{ p->
def perID = xml.'**'.find{it.personIdExternal.text() == p.personIdExternal.text()}
def pernr = xml.'**'.find{it.PERNR.text() == '000'+perID.personIdExternal.text()}
if(pernr != null)
{
perID.appendNode {
erpBenEligibility(pernr.PARDT.text()) }
}
}
message.setBody(groovy.xml.XmlUtil.serialize(xml))
样本XML:
<?xml version='1.0' encoding='UTF-8'?>
<multimap:Messages xmlns:multimap="http://sap.com/xi/XI/SplitAndMerge">
<multimap:Message1>
<person>
<person>
<street>test_stree1</street>
<city>test_city1</city>
<state>test_state1</state>
<EmpEmployment>
<personIdExternal> 001 </personIdExternal>
</EmpEmployment>
</person>
<person>
<street>test_stree2</street>
<city>test_city2</city>
<state>test_state2</state>
<EmpEmployment>
<personIdExternal> 002 </personIdExternal>
</EmpEmployment>
</person>
<person>
<street>test_stree3</street>
<city>test_city3</city>
<state>test_state3</state>
<EmpEmployment>
<personIdExternal> 003</personIdExternal>
</EmpEmployment>
</person>
</person>
</multimap:Message1>
<multimap:Message2>
<rfc:ZHR_GET_EMP_BENEFIT_DETAILS.Response xmlns:rfc="urn:sap-
com:document:sap:rfc:functions">
<phone>
<home>
<phone>number1</phone>
</home>
<PERNR> 001 </PERNR>
<PARDT>#### 1 ####</PARDT>
<home>
<phone>number2</phone>
</home>
<PERNR> 002 </PERNR>
<PARDT>#### 2 ####</PARDT>
<home>
<phone>number3</phone>
</home>
<PERNR> 003 </PERNR>
<PARDT>#### 3 ####</PARDT>
</phone>
</rfc:ZHR_GET_EMP_BENEFIT_DETAILS.Response xmlns:rfc="urn:sap-com:document:sap:rfc:functions">
</multimap:Message2>
</multimap:Messages>
您的代码中的一些主要问题:
- 使用
.**
访问器。如果 message1 中有 10000persons
,那么xml.**
将 return 一个包含count(person)+count(EmpEmployment)+count(personIdExternal) = 10000*3
个元素的数组。并在此数组上调用 findAll 应该扫描所有这些元素 - 在主循环内
xml.'**'.findAll{it.name() == 'EmpEmployment'}.each{
您无缘无故地构建了嵌套的大型数组。例如在这个表达式def perID = xml.'**'.find{it.personIdExternal.text() == p.personIdExternal.text()}
之后你有perID
等于p
您的代码仍然不符合 xml 示例。
所以,我将做一些假设来展示如何在没有 .**.
:
让我们 xml 像这样:
<?xml version='1.0' encoding='UTF-8'?>
<multimap:Messages xmlns:multimap="http://sap.com/xi/XI/SplitAndMerge">
<multimap:Message1>
<person>
<person>
<EmpEmployment>
<personIdExternal>001</personIdExternal>
</EmpEmployment>
</person>
</person>
</multimap:Message1>
<multimap:Message2>
<phone>
<xyz>
<PERNR>000001</PERNR>
<PARDT>#### 1 ####</PARDT>
</xyz>
</phone>
</multimap:Message2>
</multimap:Messages>
这是构建大型 xml 消息的代码部分:
def count = 60000 //just for test let's create xml with 60K elements
def msg = '''<?xml version='1.0' encoding='UTF-8'?>
<multimap:Messages xmlns:multimap="http://sap.com/xi/XI/SplitAndMerge">
<multimap:Message1>
<person>
'''+
(1..count).collect{"""\
<person>
<EmpEmployment>
<personIdExternal>${String.format('%03d',it)}</personIdExternal>
</EmpEmployment>
</person>
"""}.join()+
''' </person>
</multimap:Message1>
<multimap:Message2>
<phone>
'''+
(1..count).collect{"""\
<xyz>
<PERNR>${String.format('%06d',it)}</PERNR>
<PARDT>#### ${it} ####</PARDT>
</xyz>
"""}.join()+
''' </phone>
</multimap:Message2>
</multimap:Messages>
'''
现在修改后的转换算法:
def xml = new XmlParser().parseText(msg)
def t = System.currentTimeMillis()
def ns = new groovy.xml.Namespace('http://sap.com/xi/XI/SplitAndMerge')
//for fast search let map PERNR value to a node that contains it
def pernrMap=xml[ns.Message2][0].phone[0].children().collectEntries{ [it.PERNR.text(), it] }
//itearte msg1 -> find entry in pernrMap -> add node
xml[ns.Message1][0].person[0].person.each{p->
def emp = p.EmpEmployment[0]
def pernr = pernrMap['000'+emp.personIdExternal.text()]
if(pernr) emp.appendNode('erpBenEligibility', null, pernr.PARDT.text() )
}
groovy.xml.XmlUtil.serialize(xml)
println "t = ${(System.currentTimeMillis()-t)/1000} sec"
即使对于 msg1 和 msg2 中的 60k 个元素,它也会在不到 1 秒的时间内完成转换。