Online Course Graph Data Modeling for Neo4j Introduction to Graph Data Modeling Designing the Initial Graph Data Model Graph Data Modeling Core Principles Common Graph Structures Refactoring and Evolving a Model Graph Data Modeling for Neo4j: Summary Want to Speak?… Read more →

Common Graph Structures

About this module

At the end of this module, you should be able to:

  • Describe common graph structures used in modeling:
    • Intermediate nodes
    • Linked lists
    • Timeline trees
    • Multiple structures in a single graph

Intermediate nodes

You sometimes find cases where you need to connect more data to a relationship than can be fully captured in the properties. In other words, you want a relationship that connects more than two nodes. Mathematics allows this, with the concept of a hyperedge. This is impossible in Neo4j

You create intermediate nodes when you need to:

  • Connect more than two nodes in a single context.
    • Hyperedges (n-ary relationships)
  • Relate something to a relationship.

IntermediateNodesBefore

Here we have the WORKED_AT relationship that has the from and to properties, but we need to associate the role with this period of work. In Neo4j, there is no way to create a relationship that connects a relationship to a node. Relationships can only connect nodes.

Using intermediate nodes

IntermediateNodesAfter

The solution is to add a connection point in the middle of the relationship. Since nodes are connection points, you simply create a node in the middle of the relationship.

In this example, we replace the WORKED_AT relationship with an Employment intermediate node. This provides a connection point that allows us connect any amount of information to Patrick’s term of employment at Acme.

Intermediate nodes: sharing context

IntermediateNodesSharingContext

This model allows different employment events to share contextual information. Person nodes can have a shared Role or Company, and allow us to very easily trace either the full details of a single person’s career, or the overlap between different individuals.

Intermediate nodes: sharing data

IntermediateSharingData

Intermediate nodes also allow you to deduplicate information. We mentioned a similar principle when we discussed fanout where splitting a property onto its own node allows you to reference it via a relationship instead of a repeated property.

In this case, the Email intermediate node spares us having to repeat the email content on every single relationship.

Intermediate nodes: organizing data

IntermediateNodesOrganizingData

In addition, intermediate nodes can be invaluable organizing structures. This is a more expansive zoomed-out view of the same data as the previous example. The grey nodes are the Email intermediate nodes.

When we model Email as a relationship, as on the left, we encounter two problems. First, Sarah’s node becomes extremely dense. For every single recipient of any message she writes, she gains another relationship. On the right, she only has one relationship per email, regardless of the number of recipients. Dense nodes can be tricky to work with.

Second, it is difficult and expensive to determine who in Sarah’s recipient network has received a given message. In the left model, we need to traverse every relationship and check the email content to see which messages are shared. This specific case requires between six and nine “wasted” hops, depending on whether we are looking for the four-recipient message or the one-recipient message. On the right, with the Email intermediate node, we still need to check every message. But having done so, we can guarantee that every node connected to that email is a relevant one. There will only ever be three “wasted” hops.

Linked lists

Linked lists are useful whenever the sequence of objects matters. The typical pattern is to define relationships between sequential items with the names NEXT or PREVIOUS, although those words are not verbs. As always, choose one or the other based on the questions you expect to ask.

LinkedList1

Linked lists are not unique to graphs. They are a common pattern in lots of data modeling paradigms. In some of those paradigms, you see both the singly-linked list (above) and the doubly-linked list (below).

You should never use a doubly-linked list in Neo4j because doubly-linked lists use redundant symmetrical relationships.

Interleaved linked list

You can add complexity to linked lists. Sometimes, there are multiple ways you could sequence a set of items.

LinkedList2

For example, the order in which TV show episodes are shown is not always the same order in which they were created. Here, we again have the episodes of Dr. Who season 12, but with two sequences: the order of airing (NEXT) and the order of production (NEXT_IN_PRODUCTION).

Head and tail of linked list

Here is another pattern that uses a linked list. When working with linked list, there is generally a “parent” node that is used as the entry point. That parent almost always points to the first item in the sequence, using an appropriately named relationship. It may also point to the last item. For cases where you are tracking progress through the sequence, the parent node may also have a pointer to the “current” item.

LinkedList3

Some possible use cases:

  • Add episodes as they are broadcast.
  • Maintain pointer to first and last episodes.
  • Find all episodes broadcast so far.
  • Find latest episode broadcast so far.

Timeline tree

Trees are not complicated, but you will learn about one special kind of tree, the timeline tree. Timeline trees can be a useful structure when you want to use time as either an anchor or a navigational aid in your querying, and when the periods of time that interest you vary.

The topmost node in the timeline is an “all time” node. That then subdivides into whatever time periods interest you.

TimeLineTree

Here, we have chosen years, months, and days; we could just have equally chosen quarters, weeks, hours, minutes, or anything else. All of our “data” nodes then link into the timeline tree at the appropriate leaf node. Here, we have connected every purchase to a day.

With this structure, some otherwise-expensive time questions become easy. For example, finding all purchases in a given time periods, where the period of interest might be a day, a month, or a year, depending on circumstances. Simply anchor on the timeline at the appropriate time period of interest, then traverse downward through the tree.

What is more interesting, though, is dealing with time when time is part of the traversal, but not the anchor. For example, looking for all purchases that happened within a some time period relative to another event. You anchor on the specified event, traverse into the timeline tree, traverse to the appropriate time period of interest, then traverse downward. Without a timeline structure in the graph, this kind of query frequently involves a lot of property lookups and inefficient gather-and-inspect.

Note
That is not to say that timeline trees are the go-to “best” method of expressing time! Quite the opposite—​timeline trees consume a lot of space relative to the data they contain. Most often, even when time is an important part of the answer to a question, a simple property is sufficient.

But we are sharing this structure with you so that, in cases where a property alone is not good enough, you do not need to reinvent the concept of a timeline tree yourself.

Using multiple structures

All of the structures you have learned about thus far are not entire models in their own right. They are tiny patterns with specific use cases. Modeling questions as a whole are usually larger and more complex than that. Frequently, you will see multiples of these structures used together in order to fully solve a problem.

Here is one example.

MultipleStructurese

Using the timeline tree

At the top, we have a timeline tree that subdivides into years and months.

PartialTimeLineTree

Using intermediate nodes

The relevant items connected to the timeline are Employment events, which link to the starting and ending months of that employment. Employment events themselves are intermediate nodes connecting people, companies, and job roles. In this case, the intermediate nodes are serving the dual purposes of reducing the density of Person nodes, and providing an attachment point for the timeline tree.

PartialIntermediateNodes

Using linked list

PartialLinkedList

Furthermore, the Employment events themselves are organized into linked lists for each person. This allows us to represent the sequence of employment events in an individual’s career.

Exercise 4: Model a Learning Management System (LMS)

Given a description of the domain, sample data, and the application questions:

  1. Create the model (entities and connections) using the Arrow tool.
    • Look for any use cases that will use some common patterns you have learned about.
  2. Add sample data to the model using the Arrow tool and confirm questions can be answered using the model.

Exercise 4 instructions: The domain

  • There are many courses in the LMS, each of which contains a number of lessons that must be completed in a specific order.
  • Every course grants a certificate upon completion.
  • This certificate has a term of validity. When it expires, students must take the course again.
  • Students can enroll in as many simultaneous courses as they want to.
  • When a student logs in and chooses a course, the LMS must send them to their latest unfinished lesson.

Exercise 4 instructions: Sample data

Ex4-SampleData

Exercise 4 instructions: Application questions

  1. Which lesson(s) is Dan currently working on?
  2. What are Alice’s current certifications?
  3. Which lessons are in the Neo4j for Developers course?
  4. What is the last lesson in the Introduction to Neo4j course?
  5. Which lesson follows Graph Theory in the Neo4j for Developers course?
  6. Who has completed Introduction to Neo4j?

Exercise 4 solution: Application model

Ex4-Sol1

The Course and Lessons are structured as a linked list with a parent. We include the course name in the NEXT relationship, because some lessons are used by multiple courses, but in a different order. In other words, we have an interleaved linked list, and need to distinguish the different sequences.

Courses are associated with a Certificate, which contains, among other things, a term of validity expressed here in months.

Students do not directly interact with courses or lessons. Instead, we use an Enrollment intermediate node to organize their interactions. Enrollments are linked to a single course and contain a pointer to which lesson was most recently completed. When a COMPLETED relationship is created with the lesson marked LAST in a given course, that is the trigger to create an EARNED_CERTIFICATE relationship. At this point, the certExpiration property will be populated by adding the term of validity to the current timestamp. This intermediate node allows us to keep track of students’ progress through different concurrent courses, and to differentiate multiple subsequent passes through the same course.

Here is the markup for the model:

<ul class="graph-diagram-markup" data-internal-scale="0.67" data-external-scale="1">
  <li class="node" data-node-id="0" data-x="-550.4655813128935" data-y="-1216.8149073094257">
    <span class="caption">Student</span>
  </li>
  <li class="node" data-node-id="1" data-x="592.2255987630683" data-y="-808.4017464284152">
    <span class="caption">Enrollment</span><dl class="properties"><dt>certExpiration</dt><dd>date</dd></dl></li>
  <li class="node" data-node-id="2" data-x="1530.5288281421945" data-y="-188.47328130455">
    <span class="caption">Course</span>
  </li>
  <li class="node" data-node-id="3" data-x="696.1411792240782" data-y="475.60253854551536">
    <span class="caption">Lesson</span>
  </li>
  <li class="node" data-node-id="4" data-x="1562.4419933343002" data-y="475.60253854551536">
    <span class="caption">Lesson</span>
  </li>
  <li class="node" data-node-id="5" data-x="2419.99523773733" data-y="475.60253854551536">
    <span class="caption">Lesson</span>
  </li>
  <li class="node" data-node-id="6" data-x="2459.771940162705" data-y="-808.4017464284152">
    <span class="caption">Certificate</span><dl class="properties"><dt>validForMonths</dt><dd>integer</dd></dl></li>
  <li class="relationship" data-from="4" data-to="5">
    <span class="type">NEXT_&lt;course&gt;</span>
  </li>
  <li class="relationship" data-from="3" data-to="4">
    <span class="type">NEXT_&lt;course&gt;</span>
  </li>
  <li class="relationship" data-from="2" data-to="3">
    <span class="type">FIRST</span>
  </li>
  <li class="relationship" data-from="2" data-to="5">
    <span class="type">LAST</span>
  </li>
  <li class="relationship" data-from="0" data-to="1">
    <span class="type">HAS_ENROLLMENT</span>
  </li>
  <li class="relationship" data-from="1" data-to="2">
    <span class="type">IN_COURSE</span>
  </li>
  <li class="relationship" data-from="2" data-to="6">
    <span class="type">HAS_CERTIFICATE</span>
  </li>
  <li class="relationship" data-from="1" data-to="6">
    <span class="type">EARNED_CERTIFICATE</span>
  </li>
  <li class="relationship" data-from="1" data-to="4">
    <span class="type">COMPLETED</span>
  </li>
</ul>

Exercise 4 solution: Sample data

Ex4-Sol2

Here is the sample data fully fleshed out. Use the below markup to get it into Arrows.

<ul class="graph-diagram-markup" data-internal-scale="0.21" data-external-scale="1">
  <li class="node" data-node-id="0" data-x="-396.2973779224105" data-y="-1598.542651644388">
    <span class="caption">Person</span><dl class="properties"><dt>name</dt><dd>Alice</dd></dl></li>
  <li class="node" data-node-id="1" data-x="696.1411792240782" data-y="-775.5184522555546">
    <span class="caption">Enrollment</span>
  </li>
  <li class="node" data-node-id="2" data-x="1562.4419933343002" data-y="-301.66353654081996">
    <span class="caption">Course</span><dl class="properties"><dt>name</dt><dd>Introduction to Neo4j</dd></dl></li>
  <li class="node" data-node-id="3" data-x="696.1411792240782" data-y="475.60253854551536">
    <span class="caption">Lesson</span><dl class="properties"><dt>name</dt><dd>Graph Theory</dd></dl></li>
  <li class="node" data-node-id="4" data-x="1562.4419933343002" data-y="475.60253854551536">
    <span class="caption">Lesson</span><dl class="properties"><dt>name</dt><dd>Graph Databases</dd></dl></li>
  <li class="node" data-node-id="5" data-x="2419.99523773733" data-y="475.60253854551536">
    <span class="caption">Lesson</span><dl class="properties"><dt>name</dt><dd>Basic Cypher</dd></dl></li>
  <li class="node" data-node-id="6" data-x="2452.0596471356525" data-y="-970.0863461358005">
    <span class="caption">Certificate</span><dl class="properties"><dt>validForMonths</dt><dd>24</dd></dl></li>
  <li class="node" data-node-id="7" data-x="879.8886791627793" data-y="-1253.5663862086055">
    <span class="caption">Enrollment</span><dl class="properties"><dt>certExpiration</dt><dd>2020</dd></dl></li>
  <li class="node" data-node-id="8" data-x="1124.3637964049385" data-y="-1683.353885999956">
    <span class="caption">Enrollment</span><dl class="properties"><dt>certExpiration</dt><dd>2018</dd></dl></li>
  <li class="node" data-node-id="9" data-x="-591.3704658622172" data-y="-2730.474279887639"></li>
  <li class="node" data-node-id="10" data-x="-620.4774799631609" data-y="565.4384413761886">
    <span class="caption">Person</span><dl class="properties"><dt>name</dt><dd>Dan</dd></dl></li>
  <li class="node" data-node-id="11" data-x="1148.4184037393607" data-y="1678.755606466262">
    <span class="caption">Course</span><dl class="properties"><dt>name</dt><dd>Neo4j for Developers</dd></dl></li>
  <li class="node" data-node-id="12" data-x="1148.4184037393607" data-y="1015.6712674382908">
    <span class="caption">Lesson</span><dl class="properties"><dt>name</dt><dd>Property Graph</dd></dl></li>
  <li class="node" data-node-id="13" data-x="-81.87263599082607" data-y="1955.3561577156422"></li>
  <li class="node" data-node-id="14" data-x="61.062009896806536" data-y="-246.0263961108761">
    <span class="caption">Enrollment</span>
  </li>
  <li class="node" data-node-id="15" data-x="181.74861463859963" data-y="1357.3756722976887">
    <span class="caption">Enrollment</span>
  </li>
  <li class="node" data-node-id="16" data-x="2452.0596471356525" data-y="1507.105178106398">
    <span class="caption">Certificate</span><dl class="properties"><dt>validForMonths</dt><dd>6</dd></dl></li>
  <li class="relationship" data-from="4" data-to="5">
    <span class="type">NEXT_INTRO</span>
  </li>
  <li class="relationship" data-from="3" data-to="4">
    <span class="type">NEXT_INTRO</span>
  </li>
  <li class="relationship" data-from="2" data-to="3">
    <span class="type">FIRST</span>
  </li>
  <li class="relationship" data-from="2" data-to="5">
    <span class="type">LAST</span>
  </li>
  <li class="relationship" data-from="0" data-to="1">
    <span class="type">HAS_ENROLLMENT</span>
  </li>
  <li class="relationship" data-from="1" data-to="2">
    <span class="type">IN_COURSE</span>
  </li>
  <li class="relationship" data-from="2" data-to="6">
    <span class="type">HAS_CERTIFICATE</span>
  </li>
  <li class="relationship" data-from="0" data-to="7">
    <span class="type">HAS_ENROLLMENT</span>
  </li>
  <li class="relationship" data-from="0" data-to="8">
    <span class="type">HAS_ENROLLMENT</span>
  </li>
  <li class="relationship" data-from="7" data-to="2">
    <span class="type">IN_COURSE</span>
  </li>
  <li class="relationship" data-from="8" data-to="2">
    <span class="type">IN_COURSE</span>
  </li>
  <li class="relationship" data-from="8" data-to="6">
    <span class="type">EARNED_CERTIFICATE</span>
  </li>
  <li class="relationship" data-from="7" data-to="6">
    <span class="type">EARNED_CERTIFICATE</span>
  </li>
  <li class="relationship" data-from="3" data-to="12">
    <span class="type">NEXT_DEV</span>
  </li>
  <li class="relationship" data-from="12" data-to="4">
    <span class="type">NEXT_DEV</span>
  </li>
  <li class="relationship" data-from="11" data-to="3">
    <span class="type">FIRST</span>
  </li>
  <li class="relationship" data-from="11" data-to="4">
    <span class="type">LAST</span>
  </li>
  <li class="relationship" data-from="10" data-to="14">
    <span class="type">HAS_ENROLLMENT</span>
  </li>
  <li class="relationship" data-from="10" data-to="15">
    <span class="type">HAS_ENROLLMENT</span>
  </li>
  <li class="relationship" data-from="15" data-to="11">
    <span class="type">IN_COURSE</span>
  </li>
  <li class="relationship" data-from="14" data-to="2">
    <span class="type">IN_COURSE</span>
  </li>
  <li class="relationship" data-from="15" data-to="3">
    <span class="type">COMPLETED</span>
  </li>
  <li class="relationship" data-from="14" data-to="4">
    <span class="type">COMPLETED</span>
  </li>
  <li class="relationship" data-from="11" data-to="16">
    <span class="type">HAS_CERTIFICATE</span>
  </li>
</ul>

Check your understanding

Question 1

What graph modeling pattern can you use to add more contextual and organizational information to relationships that is not easily modeled with simple relationship properties?

Select the correct answer.

  • Sub-relationships.
  • Linked list.
  • Timeline tree.
  • Intermediate nodes.

Question 2

In Neo4j, which structure is not recommended as a best practice for representing linked lists?

Select the correct answer.

  • Interleaved linked lists with multiple starting points for navigation.
  • Doubly linked lists.
  • Single linked list.
  • Linked list with nodes that point to the head and tail of the list.

Question 3

What common structure is used to model data where you need to know when an event occurred in the application and find other events that occurred in the same time-frame?

Select the correct answer.

  • Linked list where each node has a timestamp.
  • Intermediate nodes where the intermediate nodes have time-related data.
  • Timeline tree
  • Doubly-linked list where the symmetric relationbship is the timestamp relationship.

Summary

You should now be able to:

  • Describe common graph structures used in modeling:
    • Intermediate nodes
    • Linked lists
    • Timeline trees
    • Multiple structures in a single graph

Stay Connected

Sign up to find out more about Neo4j's upcoming events & meetups.