Adding & reading a Union Table

The first part of this article is written in a generic fashion to show creating a Union Table using either the Schema Modeler tool on Windows or the Server Explorer tool on Mac OS X. The second part provides a platform specific example of retrieving the data from the source tables.

We are going to create a Union Table called "PhoneBook" which will have three fields (ID, Name and Phone) and two source tables (Deps and Users).

Creating a Union Table

The first step is to open the schema that you are interested in in either Schema Modeler or Server Explorer.

 

 

Click on the Unions / UNION TABLES folder in the navigation tree and then the Add New Union Table button which will open a new view where you can set the basic details of the Union Table.

 

 

Set the details as follows:

  • Union Name as PhoneBook
  • Description Phonebook of staff
  • Public leave checked
  • Read-Only check this
  • Custom Attributes leave empty
  • Default Source Table leave empty

Next expand the PhoneBook union table so the Fields and Source Tables are visible.

 

 

There are two options for creating the Union Table Fields, the first is to generate them by hand adding each one one at time and configure all of the fields as you need. The other way to do it is to drag and drop a table onto the Source Tables folder which will cause Schema Modeler / Server Explorer to automatically generate Union Table Fields based on the fields of that Source Table.

NOTE This only applies to the first table dropped onto the Source Tables folder.

For the purposes of this article we are going to use the drag & drop method. So drag the "Deps" folder (in the Tables section) onto the Source Tables folder. This will generate three fields DepId, DepName and DepPhone which we will shortly rename.

 

 

To change the details of a field, simply click on the field in the Fields folder this swaps in a view where you can change finer details of the field from its type, whether its read-only or not, its defaults value, display format and many more. More information is available in the relevant section of each tools documentation.

For our purposes, simple select each of the three fields DepId, DepName and DepPhone in turn and remove the Dep from each of the names.

 

 

Next drag the Users table down from the Tables folder and drop it onto the Source Tables folder. This adds another Source Table to our Union Table, however before we can use it the mappings between the Union Table Fields and the Source Table Fields need to be changed as the fields don't match as we need. Select the new Users source table.

Changing a mapping is rather simple, just click on the on the the Source Table Field you are interested in which will cause a popup box of all of the fields in the Source Table to appear. Click on the one that is most appropriate for your needs. Here we will change the existing mapping of the Union Table Field "Phone" from the Source Table Field "Type" to "Phone" instead.

 

 

All that remains is to save your changes to the schema document.

NOTE: When you access the Union Table for the first time, a new Union Table Field will be created called "@SourceTable" which refers to which of the Source Tables the data was retrieved from.

Accessing the Union Table

Having created the "PhoneBook" Union Table Schema Modeler / Server Explorer, the next step is to retrieve the data from it.

Accessing the data from a Union Table is exactly the same as accessing the data in a normal data table. Rather than showing how you would do this with Visual Studio, we will instead demonstrate creating a console application using the Fire IDE.

Start Fire, it displays a splash screen from which you can load an existing project or create a new project. Click Start a new Project.

On the drop down dialog, select the platform (here .NET), the Language (here Swift) and the template that the project should be based on (here Console Application) and the press OK.

Choose the location to save the project and click Create Project.

This opened the Project Window. The left side is a navigation view, the right side changes depending on what is selected on the left.

As we are working with Data Abstract we need to add a Reference to the data abstract library/framework to the project. Right click on References in the left view and click on the Add Reference button.

You need to add references to two frameworks, the first is Data Abstract and the second is Remoting SDK. Neither library is a part of the system libraries, so you need to click on Other References, then click on RemObjects.DataAbstract.dll and RemObjects.SDK.dll and finally click on the button Add 2 References.

After a momentary pause you will sees the references added to the left view. The figure below shows the completed Swift application with the added references.

The next step required is to add a licenses.licx file that the RemoteDataAdapter needs to verify that its allowed to work. In a separate editor, create a licenses.licx file and add the following line.

RemObjects.DataAbstract.RemoteDataAdapter, RemObjects.DataAbstract

Then drag that file onto the Files folder in the left pane of the Project. Right click on the licenses.licx file and change the Build Action from None to Embedded Resource

The next step is to add the code that will set up a connection to the Relativity server, log in, retrieve the data and print out the values received.

Sample Code

Available below is the sample code in all of the languages that the .NET version of Data Abstract supports. For the purposes of the example above, we used the Swift version of the code.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using RemObjects.DataAbstract;
using RemObjects.Common;
using RemObjects.SDK;

namespace ACUNetCSharp
{
    static class Program
    {
        public static Int32 Main(string[] args)
        {
            BinMessage lMessage = new BinMessage();
            IpHttpClientChannel lChannel = new IpHttpClientChannel();
            lChannel.TargetUrl = "http://localhost:7099/bin";

            RemoteService lDataService =
            new RemoteService(lMessage, lChannel, true, "DataService");

            Bin2DataStreamer lDataStreamer = new Bin2DataStreamer();
            RemoteDataAdapter lDataAdapter = new RemoteDataAdapter();
            lDataAdapter.DataStreamer = lDataStreamer;
            lDataAdapter.RemoteService = lDataService;

            // Build the login string
            String RelativityConnectionStringTemplate  = "User Id={0};Password={1};Domain={2};Schema={3}";
            String lLoginString  =
            String.Format(RelativityConnectionStringTemplate, "simple", "simple", "DASamples", "Simple");

            // Attempt to login
            bool lLogged = (new RemObjects.DataAbstract.Server.BaseLoginService_Proxy(lMessage, lChannel, "LoginService")).LoginEx(lLoginString);
            if (!lLogged)
            {
                Console.WriteLine("Cannot login. Please check the user name and password. Exiting...");
                return 1;
            }

            // Build the data request
            RemObjects.DataAbstract.Server.TableRequestInfoV5 lRequestInfo =
              new RemObjects.DataAbstract.Server.TableRequestInfoV5();
            lRequestInfo.MaxRecords = -1; // no limits - return all records
            lRequestInfo.IncludeSchema = true; // update schema in the result data table to match recieved data (not necessarily).

            DataTable lClientsData = new DataTable("PhoneBook");
            bool lApplySchema = true;

            // Retrieve the data from the server
            lDataAdapter.Fill(lClientsData, lRequestInfo, lApplySchema);

            // Print the data to the console
            foreach (DataRow lRow in lClientsData.Rows) {
                foreach (DataColumn lColumn in lClientsData.Columns) {
                    Console.Write(String.Format("{0}: {1}\t", lColumn.ColumnName, lRow.Item[lColumn]));
                }
            Console.WriteLine();
            }

        return 0;
        }
    }
}

 

namespace ACUNetOxygene;

interface

uses
  System.Linq, System.Data, RemObjects.SDK, RemObjects.DataAbstract;

type
  Program = class
  public
    class method Main(args: array of String): Int32;
  end;

implementation

class method Program.Main(args: array of String): Int32;
begin
  var lMessage := new BinMessage();
  var lChannel := new IpHttpClientChannel();
  lChannel.TargetUrl := "http://localhost:7099/bin";

  var lDataService := new RemoteService(lMessage, lChannel, true, "DataService");

  var lDataStreamer := new Bin2DataStreamer();
  var lDataAdapter := new RemoteDataAdapter();
  lDataAdapter.DataStreamer := lDataStreamer;
  lDataAdapter.RemoteService := lDataService;

  // Build the login string
  var RelativityConnectionStringTemplate := "User Id={0};Password={1};Domain={2};Schema={3}";
  var lLoginString  := String.Format(RelativityConnectionStringTemplate, "simple", "simple", "DASamples", "Simple");

  // Attempt to login
  var lLogged:Boolean := (new RemObjects.DataAbstract.Server.BaseLoginService_Proxy(lMessage, lChannel, "LoginService")).LoginEx(lLoginString);
  if lLogged = false then begin
    Console.WriteLine("Cannot login. Please check the user name and password. Exiting...");
    exit 1;
  end;

  // Build the data request
  var lRequestInfo := new RemObjects.DataAbstract.Server.TableRequestInfoV5();
  lRequestInfo.MaxRecords := -1; // no limits - return all records
  lRequestInfo.IncludeSchema := true; // update schema in the result data table to match recieved data (not necessarily).

  var lClientsData := new DataTable("PhoneBook");
  var lApplySchema := true;

  // Retrieve the data from the server
  lDataAdapter.Fill(lClientsData, lRequestInfo, lApplySchema);

  // Print the data to the console
  for each lRow:DataRow in lClientsData.Rows do begin
    for each lColumn:DataColumn in lClientsData.Columns do begin
      Console.Write(String.Format("{0}: {1}   ", lColumn.ColumnName, lRow.Item[lColumn]));
    end;

    Console.WriteLine();
  end;

  exit 0;
end;

end.

 

import System.Collections.Generic
import System.Linq
import System.Text
import System.Data
import RemObjects.DataAbstract
import RemObjects.Common
import RemObjects.SDK

// Initalise the components
let lMessage = BinMessage();
let lChannel = IpHttpClientChannel();
lChannel.TargetUrl = "http://localhost:7099/bin";

let lDataService = RemoteService(lMessage, lChannel, true, "DataService");

let lDataStreamer = Bin2DataStreamer();
let lDataAdapter = RemoteDataAdapter();
lDataAdapter.DataStreamer = lDataStreamer;
lDataAdapter.RemoteService = lDataService;

// Build the login string
let RelativityConnectionStringTemplate  =
        "User Id={0};Password={1};Domain={2};Schema={3}";
let lLoginString  =
        String.Format(RelativityConnectionStringTemplate, "simple", "simple", "DASamples", "Simple");

// Attempt to login
let lLogged = (RemObjects.DataAbstract.Server.BaseLoginService_Proxy(lMessage, lChannel, "LoginService")).LoginEx(lLoginString);
if (!lLogged)
{
    Console.WriteLine("Cannot login. Please check the user name and password. Exiting...");
    return 1;
}

// Build the data request
let lRequestInfo = RemObjects.DataAbstract.Server.TableRequestInfoV5();
lRequestInfo.MaxRecords = -1; // no limits - return all records
lRequestInfo.IncludeSchema = true; // update schema in the result data table to match received data (not necessarily).

let lClientsData = DataTable("PhoneBook");
let lApplySchema = true;

// Retrieve the data from the server            
lDataAdapter.Fill(lClientsData, lRequestInfo, lApplySchema);

// Print the data to the console
for lRow in lClientsData.Rows {
    let lTheRow:DataRow = lRow as! DataRow;
    for lColumn in lClientsData.Columns{
        let lTheColumn:DataColumn = lColumn as! DataColumn;
        print("\(lTheColumn.ColumnName): \(lTheRow.Item[lTheColumn])\t");
    }
    println();
}

return 0;

 


Sample Output

This is a sample of the executed program's output. Note the additional column @SourceTable which indicates which of the source tables that the data was retrieved from based on the order that you defined in the schema. Note that there is no programatic way to identify which table is the source table.

Id: 1   Name: Sales Phone: (873)456-78-99   @SourceTable: 0
Id: 2   Name: Incomes   Phone: (873)456-78-88   @SourceTable: 0
Id: 1   Name: Mollie Bennett    Phone: (171) 555-1212   @SourceTable: 1
Id: 2   Name: Gale Dalton   Phone: 0221-0644327 @SourceTable: 1
Id: 3   Name: Matthew Decker    Phone: (503) 555-3612   @SourceTable: 1
Id: 4   Name: Joseph Henson234  Phone: (939)399-99-99   @SourceTable: 1
Id: 5   Name: Jennifer Kent Phone: (11) 555-7647    @SourceTable: 1
Id: 6   Name: Sandy Manning Phone: (1) 354-2534 @SourceTable: 1
Id: 7   Name: Glenn Cunningham  Phone: 0522-556721  @SourceTable: 1
Id: 8   Name: Marcy Collins Phone: (14) 555-8122    @SourceTable: 1
Id: 9   Name: Kirsten Rosario   Phone: (930)930-30-93   @SourceTable: 1
Id: 10  Name: Merle Frank   Phone: (415) 555-5938   @SourceTable: 1
Id: 11  Name: Will Smith    Phone: (714) 256-0552   @SourceTable: 1
Id: 12  Name: John Doe  Phone: (612) 276-0136   @SourceTable: 1 
Id: 13  Name: Ian G. Kim    Phone: (612) 276-0136   @SourceTable: 1
Id: 14  Name: Jermaine M. Miller    Phone: (814) 343-0945   @SourceTable: 1
Id: 15  Name: Jean C. Gates Phone: (757) 382-4391   @SourceTable: 1
Id: 16  Name: David M. Lee  Phone: (704) 718-1664   @SourceTable: 1
Id: 17  Name: Jennifer K. Jones Phone: (859) 491-4222   @SourceTable: 1
Id: 18  Name: Brian L. Rowles 124   Phone: (908) 385-7809   @SourceTable: 1
Id: 19  Name: Angela W. Vanover Phone: (610) 463-9999   @SourceTable: 1
Id: 20  Name: Anna H. Kugler    Phone: (817) 249-9525   @SourceTable: 1
Id: 21  Name: Randy R. Howard   Phone: (234) 567-8909   @SourceTable: 1
Id: 22  Name: Kenny S. Lay  Phone: (817) 249-9525   @SourceTable: 1
Id: 23  Name: Maryann C. Bachmann   Phone: (907) 722-6777   @SourceTable: 1
Id: 24  Name: Lillie R. Schroeder   Phone: (312) 476-1404   @SourceTable: 1
Id: 25  Name: Samuel V. Gibbons Phone: (408) 747-3579   @SourceTable: 1