Customer Portal

Sharing a variable between disconnected nodes

Comments 5

  • Avatar
    julrych
    0
    Comment actions Permalink
    There is no way to share variables across components in CTL. And that is made on purpose.

    Dictionary is meant rather for an interface definition (for launch services) to define graph inputs, outputs and parametrization.

    What you are looking for is lookup table. Reading from lookup table can be done either using LookupJoin component or directly in CTL code using the following syntax:

    LookupTableRecordMetadata lookupMatch = lookup(MyLookupTable).get($0.LookupKey);
    if(lookupMatch != null) {
    // match found
    $0.outputField = lookupMatch.lookupValue;
    } else {
    // match not found
    }

    Writing into lookup table can be done using LookupTableReaderWriter to which you send the lookup table record.

    Reformat allows sending data to multiple outputs as well. the return value from transform() function can be:

    • SKIP - no record returned.

    • >=0 - record generated on specified output port.

    • ALL - records generated on all output ports.

    The last option is what you need.

    If there is Reformat with three output edges with the following CTL code:

    function integer transform() {
    $0.Field1 = "a";
    $1.Field2 = "b";

    // the following will not return any record on any output port.
    return SKIP;

    // the following will return record on output port 0 only.
    return 0;

    // the following will return record on output port 1 only.
    return 1;

    // the following will return record on output port 2 only (filled with default values).
    return 2;

    // the following will return records on all output ports.
    return ALL;
    }

    So, you can use reformat to branch records into several feeds; e.g. send data to LookupTableReaderWriter (you may want to use ExtFilter component in between Reformat and LookupTableReaderWriter to filter out empty records) and to regular output processing.
  • Avatar
    bfrutchey
    0
    Comment actions Permalink
    I believe you may not understand what I am attempting to do, and why the LookupTable is not effective, I will clarify below. Also, why would you not want to have the ability to pass data between nodes without exporting it in some way? That seems inefficient, although I can understand how that could cause hard-to-debug synch issues between nodes in the same phase.

    My reformater cannot send my dynamically-calculated "key-value pair" records to a different port because they are being produced from a record which needs to go to port 0. Since the transform method only has 1 return value, I can't send the record to port 0 and the discovered key-value pairs to port 1. This is further complicated because, I can't send the discovered key-value pairs to port 1 until I have processed all the incoming records (and hence emitted them to port 0).

    If you could help me with a way to "create" new records in the Reformatter this might work - i.e. I process all the incoming records and emit them to port 0, then emit "made up" records for each key-value pair (that don't correlate to a record from an input port) to port 1. I guess I could just tag the very last record emitted on port 0 with all the key-value pairs, then siphon them off with a copy/filter/reformatter to send to LookupTableReaderWriter, but how does one detect that the current record calling the transform method of the Reformatter is the last record from the input port?
  • Avatar
    julrych
    0
    Comment actions Permalink
    Can you share more details about the logic which is used to decide which records should be sent to port 1? From what you are saying, I do not think that reformat is the right component to implement this kind of processing. It actually may be more efficient to implement this functionality using set of components in batch-manner (which can be faster) rather that procedural-manner.

    Anyway, if you are still going with the procedural approach, you should be able to get it working using RollUp component. You can store the records in memory (array of records) and once all the records on input are processed and sent to output port 0, you can generate records on output port 1. Just be aware of memory needed for storing all the records.
  • Avatar
    bfrutchey
    0
    Comment actions Permalink
    Here is example code for what I am doing in my Reformatter:


    // This is the map of "normalized" soundex values found in an attribute across all records.
    // Needs to be emitted as key-value pair records for other nodes to reference.
    map[string, string] sounds;

    function integer transform() {
    // Other mappings here...
    if($0.Position_Title!=null) {
    $0.Position_Title = $0.Position_Title.trim();
    string[] parts = $0.Position_Title.split("[^A-Za-z]+");
    string delimited_soundex = "";
    foreach(string part : parts) {
    if(!part.isBlank()) {
    string sound = part.soundex();
    if(sounds[sound]==null || sounds[sound].isBlank())
    sounds[sound] = sound + " " + part;
    delimited_soundex = delimited_soundex + sounds[sound] + '${MULTIASSIGN_DELIMITER}';
    }
    }
    $0.Position_Title_Sound = delimited_soundex.replace('${MULTIASSIGN_DELIMITER}$', "");
    }
    return 0;
    }


    I hadn't thought of trying the Rollup component, I will look into that.
  • Avatar
    julrych
    0
    Comment actions Permalink
    I now see what you are trying to achieve. At the moment there are two ways to do that:

    • The way you do it currently, i.e. store sounds in memory. Once this Reformat is done, output data to LookupTableReaderWriter. In other components, you would need to access the lookup table instead of sounds array. If the other components, need to write to the lookup table, it needs to be done in a similar way as described previously.

    • Implement transformation in Java where you actually can directly write records into a lookup table. Sample code for Reformat ( I am using metadata with two string fields field1 and field2; field1 is used as a key):

      import org.jetel.component.DataRecordTransform;
      import org.jetel.data.DataRecord;
      import org.jetel.data.GetVal;
      import org.jetel.data.RecordKey;
      import org.jetel.data.SetVal;
      import org.jetel.data.lookup.Lookup;
      import org.jetel.data.lookup.LookupTable;
      import org.jetel.exception.ComponentNotReadyException;
      import org.jetel.exception.TransformException;


      public class LookupTableJava extends DataRecordTransform {

      LookupTable lookupTable;
      Lookup lookup;
      DataRecord lookupRecord;


      @Override
      public boolean init() throws ComponentNotReadyException {
      // get and initialise all structures needed for lookups
      lookupTable = getGraph().getLookupTable("LookupTable0");
      if (!lookupTable.isInitialized()) lookupTable.init();
      lookupRecord = new DataRecord(lookupTable.getKeyMetadata());
      lookupRecord.init();
      lookup = lookupTable.createLookup(new RecordKey(new String[] {"field1"}, lookupTable.getKeyMetadata()), lookupRecord);

      return super.init();
      }

      @Override
      public int transform(DataRecord[] input, DataRecord[] output) throws TransformException {

      // perform lookup
      SetVal.setString(lookupRecord, "field1", GetVal.getString(input[0], "field1"));
      lookup.seek();

      if(lookup.hasNext()) {
      // when match found in lookup table - generate output
      SetVal.setString(output[0], "field2", GetVal.getString(lookup.next() , "field2"));
      } else {
      // when match not found in lookup table - generate output
      SetVal.setString(output[0], "field2", "not found");

      // insert new record into lookup table
      DataRecord newLookupRecord = new DataRecord(lookupTable.getMetadata());
      newLookupRecord.init();
      SetVal.setString(newLookupRecord, "field1", GetVal.getString(input[0], "field1"));
      SetVal.setString(newLookupRecord, "field2", GetVal.getString(input[0], "field2"));
      // insert new record into lookup table - this is a operation not supported in CTL2
      lookupTable.put(newLookupRecord);
      }

      // generate output
      SetVal.setString(output[0], "field1", GetVal.getString(input[0], "field1"));

      return 0;
      }
      }

      By the way, I have raised a feature request CL-2016 for a CTL2 function allowing writing into lookup table.

Please sign in to leave a comment.