Over a million developers have joined DZone.

Tracking User Paths in an IVR with Neo4j

DZone's Guide to

Tracking User Paths in an IVR with Neo4j

· Java Zone ·
Free Resource

Java-based (JDBC) data connectivity to SaaS, NoSQL, and Big Data. Download Now.

I started my software development career writing applications for a Call Center at a small bank in Florida. I remember the bank had purchased whatever the “Cadillac” of Interactive Voice Response (IVR) systems was then for some crazy amount of money. Today you can build an IVR overnight using Twilio.


When you sign up with Twilio, you get to choose your phone number (more or less). For example, I picked +1 (636) 451-7411, which spells out +1 (neo) 4j1-7411. If you were to call this number right now (assuming I have not run out of Twilio credits) you’ll connect to my IVR.

A few years ago I built one for a property management firm, I’ll use that as an example.

response = Twilio::TwiML::Response.new do |r|
  r.Say "Thank you for calling the Neo4j I V R demo.", :voice => "alice"
  r.Gather :action => "/mainmenu", :numDigits => 1 do
    r.Say "For quality assurance purposes this call may be recorded", :voice => "alice"
    r.Say "For rent payment options and billing questions, press 1", :voice => "alice"
    r.Say "To submit a maintenance request, press 2", :voice => "alice"
    r.Say "For all other tenant related questions press 3", :voice => "alice"
    r.Say "To repeat this message press 9", :voice => "alice"
  r.Say "Sorry, I didn\"t get your response.", :voice => "alice"
  r.Play "http://neo-ivr.herokuapp.com/" + "hello.mp3"
  r.Redirect "/", :method => "GET"

The code above generates an XML file which Twilio interprets. If you press “2” to submit a maintenance request you’ll get another menu generated by this code:

post "/maintenance" do
  add_event(request, "/maintenance")
  response = Twilio::TwiML::Response.new do |r|
    r.Gather :action => "/maintenance-input", :numDigits => 1 do
      r.Say "if your heat is out, press 1 ", :voice => "alice"
      r.Say "if there is a problem with your plumbing, press 2", :voice => "alice"
      r.Say "for all other maintenance questions, press 3", :voice => "alice"
      r.Say "to return to the previous menu, press 0", :voice => "alice"
      r.Say "to repeat this message press 9", :voice => "alice"
    r.Say "Sorry, I didn\"t get your response.", :voice => "alice"
    r.Play "http://neo-ivr.herokuapp.com/" + "hello.mp3"
    r.Redirect "/maintenance"

IVR users will navigate through this menu and report the issues they are having or frantically start pushing buttons until they get to talk to a human being. The longer it takes them to get where they need to go the angrier they get. If only there was a way to track paths through the system… oh right… Neo4j.

The astute observer may have noticed an “add_event” method call in the code above, let’s see what that’s doing:

def add_event(request, url)
    $neo.commit_transaction(["MATCH (old:Event {session: {session}})<-[:PREV*0..]-(latest)
      WHERE NOT(latest<-[:PREV]-())
      WITH latest
      LIMIT 1
      CREATE (latest)<-[:PREV]-(e:Event {session: {session}, url: {url}})
      WITH e
      MATCH (u:User {number: {number}}), (p:Page {url: {url}})
      CREATE (u)-[:DIALED]->(e)-[:ON]->(p)
      RETURN e",
    {:number => request["From"],
     :url => url,
     :session => request["CallSid"]}

It’s telling Neo4j that a new event has occurred and that it should add it to the chain of events of this user and session. We look for the last event of this session, and create a new “Previous” relationship with the new event that points to the user and page it occurred on. As users call in, our graph gets more and more interesting and pretty soon you can start visualizing the calls to make some sense of them.


We’ll use the D3 javascript library and borrow the Sankey plugin for our visualization.


The Cypher query that generates that visualization starts from the root and goes out up to 3 hops away, counts up the paths and returns them to us. A little ruby magic turns that cypher into JSON that D3.js can understand and that’s all there is to it.

MATCH path=(p:Page {url:'/'})<-[:ON]-()-[:PREV*0..3]-()
RETURN EXTRACT(v in NODES(path)[1..LENGTH(path)+1] | v.url), count(path)
ORDER BY count(path) DESC

We can see on the image above that most people call into make a payment, and few call in to complain about plumbing. It’s one of those things that can’t really wait until next week, and most people tend to take care of it on their own.

We’ll deploy on Heroku


using the GrapheneDB plugin.

I’ve created a Twilio App that points to heroku:

…and set my number to point to the App:


Now, I don’t have a staff to take your call, and it sure isn’t going to be redirected to my cell phone, so instead I’ve decided the end of the IVR menu tree will play little sound bites from a certain “cancelled too soon sci-fi series” plus an easter egg or two thrown in there. Having problems with your heat? Maybe you should call it in: +1 (neo) 4j1-7411.

The full source code is available on github as usual. I did want to send a thank you to Nick Dingwall who did a great series on this for web traffic on his blog.

Connect any Java based application to your SaaS data.  Over 100+ Java-based data source connectors.


Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}