Customer Portal

Transposing tuples/name-value-pairs

Comments 4

  • Avatar
    avackova
    0
    Comment actions Permalink
    Hello,
    metadata can't be changed/set during the runtime. All you can do is to analyse the data (see eg. DataProfiling/graph/AdvancedStatistic.grf from our examples) and then prepare the metadata for the transformation itself.
  • Avatar
    dbrock
    0
    Comment actions Permalink
    This worked for me.
    <?xml version="1.0" encoding="UTF-8"?>
    <Graph author="Administrator" created="Wed Apr 27 15:06:53 EDT 2011" guiVersion="3.0.1" id="1306857528320" licenseCode="CLP1DENDEC30590134BY" licenseType="Commercial Pro" modified="Tue May 31 17:45:10 EDT 2011" modifiedBy="Administrator" name="DynamicDataDenormalization" revision="1.72">
    <Global>
    <Metadata fileURL="${META_DIR}/denormalizedOutput.fmt" id="Metadata1"/>
    <Metadata fileURL="${META_DIR}/denormalizedOutput.fmt" id="Metadata2" previewAttachmentCharset="ISO-8859-1"/>
    <Metadata fileURL="${META_DIR}/dynamicMetadata.fmt" id="Metadata8" previewAttachmentCharset="ISO-8859-1"/>
    <Metadata fileURL="${META_DIR}/EAV.fmt" id="Metadata0"/>
    <Metadata fileURL="${META_DIR}/EAV.fmt" id="Metadata3" previewAttachment="${DATAIN_DIR}/EAV.txt" previewAttachmentCharset="ISO-8859-1"/>
    <Metadata fileURL="${META_DIR}/EAV.fmt" id="Metadata4" previewAttachment="${DATAIN_DIR}/EAV.txt" previewAttachmentCharset="ISO-8859-1"/>
    <Metadata fileURL="${META_DIR}/test.fmt" id="Metadata5"/>
    <Metadata fileURL="${META_DIR}/test.fmt" id="Metadata7" previewAttachmentCharset="ISO-8859-1"/>
    <Metadata id="Metadata6" previewAttachmentCharset="ISO-8859-1">
    <Record fieldDelimiter="|" name="dynamic" previewAttachmentCharset="ISO-8859-1" recordDelimiter="\r\n" type="delimited">
    <Field name="Name" type="string"/>
    </Record>
    </Metadata>
    <Property fileURL="workspace.prm" id="GraphParameter0"/>
    <Note alignment="1" backgroundColorB="225" backgroundColorG="255" backgroundColorR="255" folded="false" height="171" id="Note0" textColorB="0" textColorG="0" textColorR="0" textFontSize="8" title="This phase is used to take in a tuple input file, /data-in/EAV.txt and build a metadata .fmt file dynamically using the distinct list of attributes found." titleColorB="0" titleColorG="0" titleColorR="0" titleFontSize="10" width="1051" x="1" y="44"/>
    <Note alignment="1" backgroundColorB="225" backgroundColorG="255" backgroundColorR="255" folded="false" height="151" id="Note1" textColorB="0" textColorG="0" textColorR="0" textFontSize="8" title="This phase is used to take in a tuple input file, /data-in/EAV.txt and transpose it into the new dynamically generated metadata at the output edge" titleColorB="0" titleColorG="0" titleColorR="0" titleFontSize="10" width="1016" x="11" y="277"/>
    <Dictionary/>
    </Global>
    <Phase number="0">
    <Node aggregateKey="NAME" enabled="enabled" guiHeight="0" guiName="Aggregate" guiWidth="0" guiX="470" guiY="107" id="AGGREGATE0" mapping="$Name:=$NAME;" type="AGGREGATE"/>
    <Node enabled="enabled" fileURL="${DATAIN_DIR}/EAV.txt" guiHeight="25" guiName="Input EAV File" guiWidth="50" guiX="20" guiY="107" id="DATA_READER1" skipFirstLine="true" type="DATA_READER"/>
    <Node enabled="enabled" guiHeight="0" guiName="ExtSort" guiWidth="0" guiX="245" guiY="107" id="EXT_SORT0" sortKey="NAME(a)" type="EXT_SORT"/>
    <Node enabled="enabled" fileURL="${META_DIR}/dynamicMetadata.fmt" footer="&lt;/Record&gt;" guiHeight="25" guiName="STRUCTURE_WRITER0" guiWidth="50" guiX="695" guiY="107" header="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;Record fieldDelimiter=&quot;|&quot; name=&quot;generateMetadata&quot; previewAttachmentCharset=&quot;ISO-8859-1&quot; recordDelimiter=&quot;\\r\\n&quot; skipSourceRows=&quot;0&quot; type=&quot;delimited&quot;&gt;&#13;&#10;&lt;Field name=&quot;ID&quot; type=&quot;string&quot;/&gt;&#10;" id="STRUCTURE_WRITER0" mask="&lt;Field name=\&quot;$Name\&quot; type=\&quot;string\&quot;/&gt;" type="STRUCTURE_WRITER"/>
    <Edge fromNode="AGGREGATE0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge6" inPort="Port 0 (Body port)" metadata="Metadata6" outPort="Port 0 (out)" toNode="STRUCTURE_WRITER0:0"/>
    <Edge fromNode="DATA_READER1:0" guiBendpoints="" guiRouter="Manhattan" id="Edge7" inPort="Port 0 (in)" metadata="Metadata3" outPort="Port 0 (output)" toNode="EXT_SORT0:0"/>
    <Edge fromNode="EXT_SORT0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge8" inPort="Port 0 (in)" metadata="Metadata4" outPort="Port 0 (out)" toNode="AGGREGATE0:0"/>
    </Phase>
    <Phase number="1">
    <Node enabled="enabled" fileURL="${DATAIN_DIR}/EAV.txt" guiHeight="0" guiName="UniversalDataReader" guiWidth="0" guiX="37" guiY="312" id="DATA_READER0" skipFirstLine="true" type="DATA_READER"/>
    <Node enabled="enabled" fileURL="${DATAOUT_DIR}\dynamicDataDenormalizer.out" guiHeight="0" guiName="UniversalDataWriter" guiWidth="0" guiX="487" guiY="312" id="DATA_WRITER0" outputFieldNames="true" type="DATA_WRITER"/>
    <Node enabled="enabled" guiHeight="0" guiName="Transpose" guiWidth="0" guiX="262" guiY="312" id="DENORMALIZER0" key="ID" order="Ignore" type="DENORMALIZER">
    <attr name="denormalize"><![CDATA[import java.util.Properties;

    import org.jetel.component.denormalize.DataRecordDenormalize;
    import org.jetel.data.DataRecord;
    import org.jetel.exception.ComponentNotReadyException;
    import org.jetel.exception.TransformException;
    import org.jetel.metadata.DataRecordMetadata;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Iterator;
    import java.util.ArrayList;

    public class MyDynamicDenormalize extends DataRecordDenormalize {

    private HashMap outputFieldMap;
    private String id;

    @Override
    public boolean init(Properties parameters,
    DataRecordMetadata sourceMetadata, DataRecordMetadata targetMetadata)
    throws ComponentNotReadyException {
    outputFieldMap = new HashMap();
    for (int i = 0; i < targetMetadata.getNumFields(); i++) {
    System.err.println("TargetMetadata Name: " + targetMetadata.getField(i).getName());
    if (!targetMetadata.getField(i).getName().equals("ID")) {
    outputFieldMap.put(targetMetadata.getField(i).getName(), new ArrayList<String>());
    }
    }
    return super.init(parameters, sourceMetadata, targetMetadata);
    }


    //Loop through each incoming tuple and add each value to the map
    @Override
    public int append(DataRecord inputRecord) throws TransformException {
    id = inputRecord.getField(0).toString(); // position 0 = ID
    String tupleName = inputRecord.getField(1).toString(); // position 1 = NAME
    String tupleValue = inputRecord.getField(2).toString(); // position 2 = VALUE
    if (outputFieldMap.containsKey(tupleName)) {
    ArrayList<String> valueList = (ArrayList<String>)outputFieldMap.get(tupleName);
    if (!valueList.contains(tupleValue)) {
    valueList.add(tupleValue);
    }
    outputFieldMap.put(tupleName, valueList);
    }
    return 0;
    }

    @Override
    public int transform(DataRecord outputRecord) throws TransformException {


    for (int i = 0; i < outputRecord.getNumFields(); i++) {
    String outputFieldName = outputRecord.getField(i).getMetadata().getName();
    if (!outputRecord.getField(i).getMetadata().getName().equals("ID")) {
    StringBuilder outputValue = new StringBuilder();
    if (outputFieldMap.containsKey(outputFieldName)) {
    ArrayList<String> valueList = (ArrayList<String>)outputFieldMap.get(outputFieldName);
    for (String value : valueList) {
    outputValue = outputValue.append(value).append("~");
    }
    }
    if (outputValue.length() > 0) {
    outputRecord.getField(i).setValue(outputValue.subSequence(0, outputValue.length() - 1));
    }
    }
    else{
    outputRecord.getField(i).setValue(id);
    }
    }
    return 0;
    }

    @Override
    public void clean() {
    super.clean();
    Iterator it = outputFieldMap.entrySet().iterator();
    while (it.hasNext()) {
    Map.Entry pairs = (Map.Entry)it.next();
    outputFieldMap.put(pairs.getKey(), new ArrayList<String>());
    }
    }
    }
    ]]></attr>
    </Node>
    <Edge debugMode="true" fromNode="DATA_READER0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge0" inPort="Port 0 (in)" metadata="Metadata0" outPort="Port 0 (output)" toNode="DENORMALIZER0:0"/>
    <Edge fromNode="DENORMALIZER0:0" guiBendpoints="" guiRouter="Manhattan" id="Edge2" inPort="Port 0 (in)" metadata="Metadata8" outPort="Port 0 (out)" toNode="DATA_WRITER0:0"/>
    </Phase>
    </Graph>
  • Avatar
    julrych
    0
    Comment actions Permalink
    CloverETL 3.1 implements two new components - Pivot and MetaPivot which do the transposition as from tuples to name-value pairs and back. These two components will, however, be available in commercial version only.
  • Avatar
    avackova
    0
    Comment actions Permalink
    Hello,
    the idea is good, but it doesn't work in general. The graph needs to be split to 2 graphs, as metadata can't be changed in the runtime. You can find a proper solution in the attachment.

Please sign in to leave a comment.