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 the following three 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.
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 |
Create a .NET project #
We need to build Graph Engine by following the getting started instructions before creating a Graph Engine application.
- Create a directory with name "Friends".
- Go to the newly created directory "Friends".
- Copy the "$REPO_ROOT/samples/ProjectTemplate/Template.csproj" to the "Friends" directory.
Now we can create a TSL script, say Friends.tsl, 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;
}
Next, we create a file Program.cs in the Friends directory with the content shown below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Trinity;
using Trinity.Storage;
namespace Friends
{
class Friends
{
public unsafe static void Main(string[] args)
{
}
}
}
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);
// 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>());
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.
// 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);
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.
// Marriage relationship
Monica.Spouse = Chandler.CellId;
Chandler.Spouse = Monica.CellId;
Rachel.Spouse = Ross.CellId;
Ross.Spouse = Rachel.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 Friends.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, we can make the six guys friends in our program.
// 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);
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(). The complete code is given as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Trinity;
using Trinity.Storage;
namespace Friends
{
class Friends
{
public unsafe static void Main(string[] args)
{
// 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);
Global.LocalStorage.SaveFriendship(friend_ship);
// 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);
Console.WriteLine("The character list: ");
foreach (var character in Global.LocalStorage.Character_Accessor_Selector())
{
Console.WriteLine(character.Name);
}
Console.WriteLine();
Console.WriteLine("The performer list: ");
foreach (var performer in Global.LocalStorage.Performer_Accessor_Selector())
{
Console.WriteLine(performer.Name);
}
Console.WriteLine();
long spouse_id = -1;
using (var cm = Global.LocalStorage.UseCharacter(Monica.CellId))
{
if (cm.Married)
spouse_id = cm.Spouse;
}
Console.Write("The spouse of Monica is: ");
using (var cm = Global.LocalStorage.UseCharacter(spouse_id))
{
Console.WriteLine(cm.Name);
}
Console.WriteLine("Press any key to exit ...");
Console.ReadKey();
}
}
}
The lines after Global.LocalStorage.SaveStorage() are used to verify the data we have stored. We will explain them in the later chapters.
Building and running the project #
- Run
dotnet restore
- Run
dotnet run
If no errors occur, we are going to see the console output shown below:
The character list:
Monica Geller
Phoebe Buffay
Ross Geller
Chandler Bing
Rachel Green
Joey Tribbiani
The performer list:
Lisa Kudrow
Courteney Cox
Matt Le Blanc
David Schwimmer
Matthew Perry
Jennifer Aniston
The spouse of Monica is: Chandler Bing
Press any key to exit ...