Upgrade to Pro — share decks privately, control downloads, hide ads and more …

It's 2017, and I still want to sell you a graph...

It's 2017, and I still want to sell you a graph database

The aha!s and the oh-noe!s of over one year of building our product with a graph database, Neo4j, along with big brother PostgreSQL and hipster cousin Redis with Rails.

This talk will attempt to answer an important question, "when does using a graph database make sense?", through retrospection.

Swanand Pagnis

January 29, 2017
Tweet

More Decks by Swanand Pagnis

Other Decks in Technology

Transcript

  1. A LANGUAGE THAT DOESN'T AFFECT THE WAY YOU THINK ABOUT

    PROGRAMMING IS NOT WORTH KNOWING. Alan Perlis, emphasis mine.
  2. A DATABASE THAT DOESN'T AFFECT THE WAY YOU THINK ABOUT

    DATA MODELLING IS NOT WORTH KNOWING. Alan Perlis, paraphrased
  3. GRAPH SEMANTICS SYMMETRICAL RELATIONSHIPS: MARRIAGES class Person < ActiveRecord::Base
 has_many

    :marriages
 has_many :spouses, through: :marriages, source: :spouse
 end
 class Marriage < ActiveRecord::Base
 belongs_to :person
 belongs_to :spouse, foreign_key: :spouse_id, class_name: "Person"
 end
  4. GRAPH SEMANTICS SYMMETRICAL RELATIONSHIPS: MARRIAGES class Marriage < ActiveRecord::Base
 belongs_to

    :person
 belongs_to :spouse, foreign_key: :spouse_id, class_name: "Person"
 
 after_create do
 inverse.first_or_create
 end
 
 after_destroy do
 inverse.first.try(:destroy)
 end
 
 def inverse
 Marriage.where(person: spouse, spouse: person)
 end
 end
  5. GRAPH SEMANTICS SYMMETRICAL RELATIONSHIPS: MARRIAGES class Person
 include Neo4j::ActiveNode
 


    # (Person)-[:MARRIES]->(Person)
 has_many :both, :spouses,
 type: "MARRIES", model_class: "Person"
 
 # (Person)-[:IS_MARRIED_TO]->(Person)
 has_one :both, :current_spouse,
 type: "IS_MARRIED_TO", model_class: "Person"
 end
  6. GRAPH SEMANTICS SYMMETRICAL RELATIONSHIPS: MARRIAGES class Person
 include Neo4j::ActiveNode
 


    # (Person)-[:MARRIES]->(Person)
 has_many :both, :spouses,
 type: "MARRIES", model_class: "Person"
 
 # (Person)-[:IS_MARRIED_TO]->(Person)
 has_one :both, :current_spouse,
 type: "IS_MARRIED_TO", model_class: "Person"
 end
  7. GRAPH SEMANTICS AD-HOC POINTERS # (Author)-[:HAS_WRITTEN]->(Book)
 has_many :out, :books,
 type:

    "HAS_WRITTEN", model_class: "Book"
 
 
 # (Author)-[:IS_WRITING]->(Book)
 has_one :out, :current_book,
 type: "IS_WRITING", model_class: "Book"
  8. GRAPH SEMANTICS AD-HOC POINTERS # (Student)-[:HAS_ATTEMPTED]->(Assignment)
 has_many :out, :assignments,
 type:

    "HAS_ATTEMPTED", model_class: "Assignment"
 
 
 # (Student)-[:HAS_BEST]->(Assignment)
 has_one :out, :best_submission,
 type: "HAS_BEST", model_class: "Assignment"
  9. GRAPH SEMANTICS AD-HOC POINTERS # (Dentist)-[:TREATS]->(Patient)
 has_many :out, :patients,
 type:

    "TREATS", model_class: "Person"
 
 
 # (Dentist)<-[:TEACHES_RUBY]-(Patient)
 has_one :in, :ruby_teacher,
 type: "TEACHES_RUBY", model_class: "Person"
  10. GRAPH SEMANTICS AD-HOC POINTERS # (Person)-[:HAS]->(MoveScore) has_many :out, :move_scores, type:

    "HAS", model_class: "MoveScore"
 # (Person)-[:HAS_CURRENT]->(MoveScore) has_one :out, :current_move_score, type: "HAS_CURRENT", model_class: "MoveScore"
 # (Person)-[:HAS_PREFERRED]->(*) has_one :out, :preferred_move_score, type: "HAS_PREFERRED", model_class: false
  11. GRAPH SEMANTICS AD-HOC POINTERS MATCH
 (realtor:Person)<-[:CONTACT_OF]-(contact:Person)<- [:CONTACT_INFOS]-(email:EmailContactInfo)
 WHERE
 (contact.rapportive_enqueued IS

    NULL)
 AND NOT ((contact)<-[:SOCIAL_PROFILES]- (:SocialProfile {type: 'linked_in'}))
 RETURN
 DISTINCT(contact) as contact, realtor.user_id as user_id, collect(email) as emails
  12. GRAPH SEMANTICS AD-HOC POINTERS MATCH
 (realtor:Person)<-[:CONTACT_OF]-(contact:Person)<- [:CONTACT_INFOS]-(email:EmailContactInfo)
 WHERE
 (contact.rapportive_enqueued IS

    NULL)
 AND NOT ((contact)<-[:SOCIAL_PROFILES]- (:SocialProfile {type: 'linked_in'}))
 RETURN
 DISTINCT(contact) as contact, realtor.user_id as user_id, collect(email) as emails
  13. GRAPH SEMANTICS AD-HOC POINTERS # (Person)-[:HAS]->(MoveScore) has_many :out, :move_scores, type:

    "HAS", model_class: "MoveScore"
 # (Person)-[:HAS_CURRENT]->(MoveScore) has_one :out, :current_move_score, type: "HAS_CURRENT", model_class: "MoveScore"
 # (Person)-[:HAS_PREFERRED]->(*) has_one :out, :preferred_move_score, type: "HAS_PREFERRED", model_class: false
  14. GRAPH SEMANTICS AD-HOC POINTERS # (Person)-[:HAS]->(MoveScore) has_many :out, :move_scores, type:

    "HAS", model_class: "MoveScore"
 # (Person)-[:HAS_CURRENT]->(MoveScore) has_one :out, :current_move_score, type: "HAS_CURRENT", model_class: "MoveScore"
 # (Person)-[:HAS_PREFERRED]->(*) has_one :out, :preferred_move_score, type: "HAS_PREFERRED", model_class: false
  15. GRAPH SEMANTICS AD-HOC POINTERS # (Person)-[:HAS]->(MoveScore) has_many :out, :move_scores, type:

    "HAS", model_class: "MoveScore"
 # (Person)-[:HAS_CURRENT]->(MoveScore) has_one :out, :current_move_score, type: "HAS_CURRENT", model_class: "MoveScore"
 # (Person)-[:HAS_PREFERRED]->(*) has_one :out, :preferred_move_score, type: "HAS_PREFERRED", model_class: false
  16. GRAPH SEMANTICS AD-HOC POINTERS, POLYMORPHIC # Post, Article, Status has_many

    :comments, as: :commentable # Comment belongs_to :commentable, polymorphic: true
  17. GRAPH SEMANTICS EXAMPLE: SIX DEGREES OF KEVIN BACON (Actor) -[:HAS_WORKED_WITH]->(Actor)

    -[:HAS_WORKED_WITH]->(Actor) -[:HAS_WORKED_WITH]->(Actor) -[:HAS_WORKED_WITH]->(Actor) -[:HAS_WORKED_WITH]->(Actor)
  18. GRAPH SEMANTICS EXAMPLE: SIX DEGREES OF KEVIN BACON Answer to

    this question has been left to the reader as an exercise.
  19. GRAPH SEMANTICS CYPHER THE QUERY LANGUAGE MATCH (you {name:"You"})
 MATCH

    (expert) -[:WORKED_WITH]-> (db:Database {name:"Neo4j"})
 MATCH path = shortestPath( (you)-[:FRIEND*..5]-(expert))
 RETURN db,expert,path
  20. TOOLING NEO4J.RB API # (Person)-[:HAS]->(MoveScore) has_many :out, :move_scores, type: "HAS",

    model_class: "MoveScore"
 # (Person)-[:HAS_CURRENT]->(MoveScore) has_one :out, :current_move_score, type: "HAS_CURRENT", model_class: "MoveScore"
 # (Person)-[:HAS_PREFERRED]->(*) has_one :out, :preferred_move_score, type: "HAS_PREFERRED", model_class: false
  21. TOOLING NEO4J.RB API: ALL YOUR FAMILIAR TOOLS ▸ Properties ▸

    Indexes / Constraints ▸ Callbacks ▸ Validation ▸ Associations
  22. TOOLING NEO4J: NO TYPES # This is a sorted collection


    [2, 3, 5, 7, 11]
 
 # And so is this
 ["11", "2", "3", "5", "7"]
  23. TOOLING NEO4J: ALL REST, NO BINARY Neo4j::Session::CypherError
 # 1. Unable

    to load RELATIONSHIP with id <some-id>
 # 2. LockClient[80] can't wait on resource
 # 3. Expected a numeric value for empty iterator, but got null
  24. TOOLING NEO4J: ALL REST, NO BINARY Faraday::ConnectionFailed
 # 1. too

    many connection resets
 # 2. connection refused to: <ip-address>
 # 3. Failed to open TCP connection to
 # 4. execution expired