Friends Graph

Source Code on GitHub #

This example is to illustrate how to model and store data in GE. In this "hello world" application, we model and store a small social graph of the six main characters in the famous situation comedy Friends.

We model two types of entities in this example. One is the Characters in the TV series, the other is their Performers. Friends has six main characters as shown in the Characters table. There are many relations between these entities. In this example, we focus on three representative relationships: the marriage relationship between two characters, the portray relationship between a performer and a Character, and the friendship relationship between two characters. The properties of each entity are shown below.

Table 1. Characters

Name

Gender

Married

Spouse

Cast

Rachel Green

Female

true

Ross Geller

Jennifer Aniston

Monica Geller

Female

true

Chandler Bing

Courteney Cox

Phoebe Buffay

Female

true

Mike Hannigan

Lisa Kudrow

Joey Tribbiani

Male

false

N/A

Matt Le Blanc

Chandler Bing

Male

true

Monica Geller

Matthew Perry

Ross Geller

Male

true

Rachel Green

David Schwimmer

We first create a TSL script to model the data shown in the table. The script defines the schema of the data.

cell struct Character
{
    String Name;
    byte Gender;
    bool Married;
    long Spouse;
    long Performer;
}

cell struct Performer
{
    String Name;
    int Age;
    List<long> Characters;
}

In Visual Studio, we can now New a Graph Engine Project called FriendsCell. We copy and paste the tsl script shown above into the default TSL file.

Next, we New a Graph Engine Application Project called Friends. Open the generated Program.cs of Friends project, add the following namespaces to the preamble:

using Trinity;
using Trinity.Extension;

A client-side Trinity program has two running modes. One is Embedded mode, and the other is Client mode. Let us go with the simpler embedded mode first. We can instruct Trinity to run in embedded mode by putting the following line at the beginning of the program.

TrinityConfig.CurrentRunningMode = RunningMode.Embedded;

Now we can get into the real business. There are six characters and six corresponding performers. We first create 12 entity cells for them:

// Characters
Character Rachel = new Character(Name: "Rachel Green", Gender: 0, Married: true);
Character Monica = new Character(Name: "Monica Geller", Gender: 0, Married: true);
Character Phoebe = new Character(Name: "Phoebe Buffay", Gender: 0, Married: true);
Character Joey = new Character(Name: "Joey Tribbiani", Gender: 1, Married: false);
Character Chandler = new Character(Name: "Chandler Bing", Gender: 1, Married: true);
Character Ross = new Character(Name: "Ross Geller", Gender: 1, Married: true);

// Cast
Performer Jennifer = new Performer(Name: "Jennifer Aniston", Age: 43, 
Characters: new List<long>());
Performer Courteney = new Performer(Name: "Courteney Cox", Age: 48, 
Characters: new List<long>());
Performer Lisa = new Performer(Name: "Lisa Kudrow", Age: 49, 
Characters: new List<long>());
Performer Matt = new Performer(Name: "Matt Le Blanc", Age: 45, 
Characters: new List<long>());
Performer Matthew = new Performer(Name: "Matthew Perry", Age: 43, 
Characters: new List<long>());
Performer David = new Performer(Name: "David Schwimmer", Age: 45, 
Characters: new List<long>());

Now we define a portrayal relationship to illustrate how we represent directed relationships. A Portrayal relationship is the relationship between a performer and a character. It is about who performs a character. For example, Jennifer portrays the character of Rachel.

Rachel.Performer = Jennifer.CellID;
Jennifer.Characters.Add(Rachel.CellID);

Then, we define an undirected marriage relationship to illustrate how undirected relationships are represented. For example, Monica and Chandler are spouse of each other in the show.

Monica.Spouse = Chandler.CellID;
Chandler.Spouse = Monica.CellID;

We can easily model multilateral relationships as well. To illustrate this, let us consider the friends relationship between these six characters. All these six characters are friends with each other. That means we need to create 15 friends relationship edges between each pair of them. How to simplify this?

In Graph Engine, we can create a hyperedge cell called Friendship and connect all these six characters using this cell. To do this, we extend the MyCell.tsl script a bit. We add the following three lines to this file to create a Friendship cell.

cell struct Friendship
{
    List<long> friends;
}

After adding the Friendship cell definition, recompile the FriendsCell project. Now we can make the six guys friends in our program.

Friendship friend_ship = new Friendship();
friend_ship.friends.Add(Rachel.CellID);
friend_ship.friends.Add(Monica.CellID);
friend_ship.friends.Add(Phoebe.CellID);
friend_ship.friends.Add(Joey.CellID);
friend_ship.friends.Add(Chandler.CellID);
friend_ship.friends.Add(Ross.CellID);

So far so good. We have 12 entity cells and we define three relationships between these entities. But wait, we are not done yet. All these cells now are no more than 12 .Net objects on .Net runtime heap. It is neither in Trinity's managed main memory storage, nor persisted on disk files.

Let us see how to make data persistent in Trinity. Cell is the basic data unit in Trinity. A cell may exist in three forms.

  • As a .Net object on .Net runtime heap.
  • As a blob of bytes in Trinity's memory storage.
  • As a blob of bytes in disk files.

For now, the cells we created are on the .Net runtime heap. To leverage the true power of Trinity, we need to save these cells to Trinity's main memory storage. The reasons why we need to do this was discussed in Accessors. With cells stored in the Trinity memory storage, we have thread-safe cell manipulation guarantee without losing the convenience of object-oriented cell accessing interfaces.

Runtime objects can be easily converted into cells resident in Trinity memory storage. We can save a Performer cell by calling Global.LocalStorage.SavePerformer(performer) and save a Character cell by calling Global.LocalStorage.SaveCharacter(character) as follows.

Global.LocalStorage.SavePerformer(Jennifer);
Global.LocalStorage.SaveCharacter(Rachel);

Once the data is in Trinity's LocalStorage, we can persist the data to the disk by simply calling Global.LocalStorage.SaveStorage(). Below is a complete List of our first hello world Trinity program.

using System;
using System.Collections.Generic;
using System.Text;

using Trinity;
using Trinity.Data;
using Trinity.Storage;

namespace Friends
{
    class Friends
    {
        public unsafe static void Main(string[] args)
        {
            TrinityConfig.CurrentRunningMode = RunningMode.Embedded;

            // Characters
            Character Rachel = new Character(Name: "Rachel Green", Gender: 0, 
            Married: true);
            Character Monica = new Character(Name: "Monica Geller", Gender: 0, 
            Married: true);
            Character Phoebe = new Character(Name: "Phoebe Buffay", Gender: 0, 
            Married: true);
            Character Joey = new Character(Name: "Joey Tribbiani", Gender: 1, 
            Married: false);
            Character Chandler = new Character(Name: "Chandler Bing", Gender: 1,
            Married: true);
            Character Ross = new Character(Name: "Ross Geller", Gender: 1, 
            Married: true);

            // Performers
            Performer Jennifer = new Performer(Name: "Jennifer Aniston", Age: 43,
            Characters: new List<long>());
            Performer Courteney = new Performer(Name: "Courteney Cox", Age: 48, 
            Characters: new List<long>());
            Performer Lisa = new Performer(Name: "Lisa Kudrow", Age: 49, 
            Characters: new List<long>());
            Performer Matt = new Performer(Name: "Matt Le Blanc", Age: 45, 
            Characters: new List<long>());
            Performer Matthew = new Performer(Name: "Matthew Perry", Age: 43, 
            Characters: new List<long>());
            Performer David = new Performer(Name: "David Schwimmer", Age: 45, 
            Characters: new List<long>());

            // Portrayal Relationship
            Rachel.Performer = Jennifer.CellID;
            Jennifer.Characters.Add(Rachel.CellID);

            Monica.Performer = Courteney.CellID;
            Courteney.Characters.Add(Monica.CellID);

            Phoebe.Performer = Lisa.CellID;
            Lisa.Characters.Add(Phoebe.CellID);

            Joey.Performer = Matt.CellID;
            Matt.Characters.Add(Joey.CellID);

            Chandler.Performer = Matthew.CellID;
            Matthew.Characters.Add(Chandler.CellID);

            Ross.Performer = David.CellID;
            David.Characters.Add(Ross.CellID);

            // Marriage relationship
            Monica.Spouse = Chandler.CellID;
            Chandler.Spouse = Monica.CellID;

            Rachel.Spouse = Ross.CellID;
            Ross.Spouse = Rachel.CellID;

            // Friendship
            Friendship friend_ship = new Friendship(new List<long>());
            friend_ship.friends.Add(Rachel.CellID);
            friend_ship.friends.Add(Monica.CellID);
            friend_ship.friends.Add(Phoebe.CellID);
            friend_ship.friends.Add(Joey.CellID);
            friend_ship.friends.Add(Chandler.CellID);
            friend_ship.friends.Add(Ross.CellID);

            // Save Runtime cells to Trinity memory storage
            Global.LocalStorage.SavePerformer(Jennifer);
            Global.LocalStorage.SavePerformer(Courteney);
            Global.LocalStorage.SavePerformer(Lisa);
            Global.LocalStorage.SavePerformer(Matt);
            Global.LocalStorage.SavePerformer(Matthew);
            Global.LocalStorage.SavePerformer(David);

            Global.LocalStorage.SaveCharacter(Rachel);
            Global.LocalStorage.SaveCharacter(Monica);
            Global.LocalStorage.SaveCharacter(Phoebe);
            Global.LocalStorage.SaveCharacter(Joey);
            Global.LocalStorage.SaveCharacter(Chandler);
            Global.LocalStorage.SaveCharacter(Ross);

            // Dump memory storage to disk for persistence
            Global.LocalStorage.SaveStorage();

            long spouse_id = -1;

            using (var cm = Global.LocalStorage.UseCharacter(Monica.CellID))
            {
                if (cm.Married)
                    spouse_id = cm.Spouse;
            }

            using (var cm = Global.LocalStorage.UseCharacter(spouse_id))
            {
                Console.WriteLine(cm.Name);
            }
        }
    }
}

The lines after Global.LocalStorage.SaveStorage() are used to verify the data we have stored. We will explain them in the later chapters.