Graph database nodes and edges change over time

Viewed 287

I would like to map the German Bundesliga as a structure in a graph database. This is no problem at all for the current "static" status:

  • Players are linked to clubs.
  • Clubs are linked to each other via matches.
  • Clubs are assigned to leagues.

However, I would like to bring in the dimension of time:

  • The 3rd League, for example, has only existed since 2008. Before that, the leagues were structured quite differently.
  • Players change clubs

How could I map this in a graph database? Unfortunately, I haven't found anything in multi-model databases like OrientDB.

2 Answers

He typical approach is to us validFrom/validTo attributes on vertices and edges. Then you can filter on these attributes when querying the graph.

Eg.

CREATE VERTEX Club SET name = 'AS Roma';
CREATE VERTEX Player name = 'Francesco', surname = 'Totti';
CREATE EDGE PlaysWith
  FROM (SELECT FROM Player WHERE surname = 'Totti)
  TO (SELECT FROM Club WHERE name = 'AS Roma')
  SET
  validFrom = date('1992-01-01', 'yyyy-MM-dd')
  validTo = date('2017-12-032', 'yyyy-MM-dd');

MATCH {class:Player, as:p}
      .outE("PlaysWith"){where:(
          validFrom < date('2001-01-01', 'yyyy-MM-dd')
          validTo > date('2001-01-01', 'yyyy-MM-dd')
      )}
      .inV(){as:Club, where:(name = "AS Roma")}
RETURN $elements;

Adding a time dimension nodes and edges in a graph database is a pretty common design pattern. It allows you to capture all states of the data and then view the data at a particular point in time or within a time range.

Here is an InfiniteGraph solution:

UPDATE SCHEMA {
   CREATE CLASS Team {
      name    : String,
      players : LIST {
                    element: Reference {
                    edgeClass  : PlaysFor,
                    edgeAttribute   : player
                    }, 
                  CollectionTypeName : SegmentedArray
                }
  }
  CREATE CLASS Player {
     name      : String,
     playsFor  : LIST {
                    element: Reference {
                       edgeClass : PlaysFor,
                       edgeAttribute  : team
                    },
                    CollectionTypeName : SegmentedArray
                  }
  }
  CREATE CLASS PlaysFor {
      dateFrom : Date,
      dateTo   : Date,
      team     : Reference {referenced: Team, inverse: players },
      player   : Reference {referenced: Player,  inverse: playsFor }
   }
};
//---------------------------------- Result ------------------------------------
Done.

Create some player and team data...

let teamHKiel = CREATE Team { name: "Holstein Kiel" };   
let teamMainz = CREATE Team { name: "FVZ Mainz 05 II" };
let teamSpVgg = CREATE Team { name: "SpVgg Unterhaching" };

let playerRZ = CREATE Player { name: "Robin Zentner" };
let playerAH = CREATE Player { name: "Alexander Hack" };

Link the players to their teams for specific date ranges.

CREATE PlaysFor { 
      player: $playerRZ, team: $teamMainz, 
      dateFrom: 2014-08-02, dateTo: 2015-08-01 
};
//---------------------------------- Result ------------------------------------
3-3-1-14


CREATE PlaysFor { 
       player: $playerRZ, team: $teamHKiel, 
       dateFrom: 2015-08-02, dateTo: 2016-08-01 
};
//---------------------------------- Result ------------------------------------
3-3-1-19

CREATE PlaysFor { 
       player: $playerAH, team: $teamMainz, 
       dateFrom: 2014-07-02, dateTo: 2017-06-01 
};
//---------------------------------- Result ------------------------------------
3-3-1-22

CREATE PlaysFor { 
       player: $playerAH, team: $teamSpVgg, 
       dateFrom: 2013-07-02, dateTo: 2014-07-01 
};
//---------------------------------- Result ------------------------------------
3-3-1-25

Which team was Robin Zentner playing for on 2016-05-01?

LET d = 2016-05-01;
MATCH path = (p:Player {name == 'Robin Zentner'}) 
             -[:PlaysFor {dateFrom < $d AND dateTo > $d}]->(t:Team) 
        RETURN p.name as player, t.name as team;
//---------------------------------- Result ------------------------------------
{
  _Projection
  {
    player:'Robin Zentner',
    team:'Holstein Kiel'
  }
}

Set a different date.

//---------------------------------- Statement ---------------------------------
// LET d = 2015-05-01;
//---------------------------------- Result ------------------------------------
Done.

Which team was Robin Zentner playing for on 2015-05-01?

MATCH path = (p:Player {name == 'Robin Zentner'}) 
            -[:PlaysFor {dateFrom < $d AND dateTo > $d}]->(t:Team) 
            RETURN p.name as player, t.name as team;
//---------------------------------- Result ------------------------------------                  
{
  _Projection
  {
    player:'Robin Zentner',
    team:'FVZ Mainz 05 II'
  }
}

Who was playing for FVZ Mainz 05 II on 2015-03-01?

LET d = 2015-03-01;
//---------------------------------- Result ------------------------------------
Done.

MATCH path = (t:Team {name == 'FVZ Mainz 05 II'}) 
             -[:PlaysFor {dateFrom < $d AND dateTo > $d}]-> 
             (p:Player) 
             RETURN p.name as player, t.name as team;
//---------------------------------- Result ------------------------------------

{
  _Projection
  {
    player:'Robin Zentner',
    team:'FVZ Mainz 05 II'
  },
  _Projection
  {
    player:'Alexander Hack',
    team:'FVZ Mainz 05 II'
  }
}

#InfiniteGraph

Related