Over a million developers have joined DZone.

An Interesting Example of Imperative Versus Functional Thinking with Neo4j

DZone's Guide to

An Interesting Example of Imperative Versus Functional Thinking with Neo4j

· Database Zone
Free Resource

Learn how to create flexible schemas in a relational database using SQL for JSON.

Over the weekend I was trying to port some of the neo4j import code for the ThoughtWorks graph I’ve been working on to make use of the REST Batch API and I came across an interesting example of imperative vs functional thinking.

I’m using the neography gem to populate the graph and to start with I was just creating a person node and then creating an index entry for it:

people_to_load = Set.new
people_to_load << { :name => "Mark Needham", :id => 1 }
people_to_load << { :name => "Jenn Smith", :id => 2 }
people_to_load << { :name => "Chris Ford", :id => 3 } 
command_index = 0
people_commands = people_to_load.inject([]) do |acc, person| 
  acc << [:create_node, {:id => person[:id], :name => person[:name]}]
  acc << [:add_node_to_index, "people", "name", person[:name], "{#{command_index}}"]
  command_index += 2
Neography::Rest.new.batch * people_commands

people_commands ends up containing the following arrays in the above example:

  [:create_node, {:id=>"1", :name=>"Mark Needham"}], 
  [:add_node_to_index, "people", "name", "Mark Needham", "{0}"], 
  [:create_node, {:id=>"2", :name=>"Jenn Smith"}], 
  [:add_node_to_index, "people", "name", "Jenn Smith", "{2}"], 
  [:create_node, {:id=>"3", :name=>"Chris Ford"}, 
  [:add_node_to_index, "people", "name", "Chris Ford", "{4}"]

We can refer to previously executed batch commands by referencing their ‘job id’ which in this case is their 0 indexed position in the list of commands. e.g. the second command which indexes me refers to the node created in ‘job id’ ’0′ i.e the first command in this batch

In the neo4j REST API we’d be able to define an arbitrary id for a command and then reference that later on but it’s not implemented that way in neography.

I thought having the ‘command_index += 2′ was a bit rubbish because it’s nothing to do with the problem I’m trying to solve so I posted to twitter to see if there was a more functional way to do this.

My colleague Chris Ford came up with a neat approach which involved using ‘each_with_index’ to work out the index positions rather than having a counter. His final version looked like this:

insert_commands = people_to_load.map do |person|
  [:create_node, {:id => person[:id], :name => person[:name]}]
index_commands = people_to_load.each_with_index.map do |person, index|
  [:add_node_to_index, "people", "name", person[:name], "{#{index}}"]
people_commands = insert_commands + index_commands

The neat thing about this solution is that Chris has separated the two concerns – creating the node and indexing it.

When I was thinking about this problem imperatively they seemed to belong together but there’s actually no reason for that to be the case and we can write simpler code by separating them.

We do iterate through the set twice but since it’s not really that big it doesn’t make too much difference. to the performance.

Create flexible schemas using dynamic columns for semi-structured data. Learn how.


Published at DZone with permission of Mark Needham, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.


Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.


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

{{ parent.tldr }}

{{ parent.urlSource.name }}