Intro to Neo4jrb 3.0 (NYC Neo4j Meetup 11/19/2014)

Post on 13-Jul-2015

120 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

Transcript

Neo4jrb 3 is awesome

Prepared for the NYC Neo4j Meetup, November 19, 2014

by Chris Griggtotally biased co-maintainer

Prepared for the NYC Neo4j Meetup, November 19, 2014

In this presentation...

We're going to talk Neo4j and Ruby.

In case you're new to either, here's the quickest summary you'll ever get of both.

Basic data modeling in Neo4jNeo4j is a pure graph database.

SQL tables and relationships.

Image from http://i.stack.imgur.com/LmARq.png

A table becomes a label (Task and Tag), a row becomes a node, a column becomes a property, a join table becomes a relationship. The relationship is part of the data, so once we have a single Task, finding its tags is fast and easy. Relationships can have their own properties, too.

Image generated using http://www.apcjones.com/arrows/

Cypher is Neo4j's Query Language"Return the tags of a specific task…"

SQL:SELECT * FROM 'Tag' JOIN 'TaskTag' ON ('Tag'.TagID = 'TaskTag'.TagID) where 'TaskTag'.TaskID = 1;

Cypher:MATCH (task:Task)-[r:TAGGED_WITH]->(tag:Tag) WHERE ID(task) = 1 RETURN tag

Ruby...● ...is a dynamically typed programming language with a

syntax aimed at making developers happy.

● ...is known for (among other things) low learning curve, huge and colorful community, and tools that enable rapid development.

● ...calls its packages "gems."

What is Neo4jrb?A project that makes Ruby devs happy and Neo4j easier.

● neo4j-core gem handles low-level DB communication, provides basic Neo4j API and Cypher DSL.

● neo4j gem adds object-graph-mapper (OGM) features. It's like Mongoid or ActiveRecord but for Neo4j.

But usually "Neo4jrb" means "the neo4j gem."

Oh yeah, and......we won an award from Neo Technology at the 2014 Graph Connect conference for Best Community Contribution.

We think that's pretty cool and we'll bring it up until it gets weird.

OK, so what do these gems do?They provide abstractions. If you start with this:MATCH (s:`Student`)-[r1:`ENROLLED_IN`]->(l:`Lesson`) WHERE s.name = 'Chris' RETURN s

neo4j-core lets you write it like this:Neo4j::Session.query.match('(s:`Student`)-[r1:`ENROLLED_IN`]->(l:`Lesson`)').where(s: { name: 'Chris' }).pluck(:s)

neo4j (neo4jrb) lets you simplify it to this:Student.as(:s).lessons.where(s: { name: 'Chris' }).pluck(:s)

But wait, there's more!

Neo4jrb was modeled after ActiveRecord. That means...● Node models. Declared properties, validations,

callbacks, methods, finders.● Relationship models. Like node models, but they hold

your relationship logic.● Expressive Cypher DSL. You can build entire apps

without ever writing a line of Cypher directly.● Low, low cost of entry. Know Rails and ActiveRecord?

You can use Neo4j!

No, you don't have to use JRuby.

Neo4jrb 2 required JRuby and Neo4j Embedded. This made things complicated.Neo4jrb 3 supports Ruby MRI with Neo4j Server AND JRuby with Neo4j embedded with the same API. That means:● Want to use the Neo4j Java API and Torquebox? No

problem.● Want the ease of Ruby MRI, flexibility of Neo4j Server?

Maybe Heroku or a different PaaS? You're covered.

Why use an O*M?● Protect your code from query language changes.● Usually more comfortable for devs: write your Ruby app

in Ruby instead of Ruby + Cypher.● Encourages best practices.● Shortcuts for boilerplate code.● ...and many more, but we're on a time limit here.

OGM VS pure Cypher. Why? Part 1.Any O*M makes the most repetitive parts of an app less of a chore. Cypher:CREATE (s:Student { name: 'Chris', age: 30 }) RETURN s

CREATE (l:Lesson { subject: 'Math', level: 99 }) RETURN l

MATCH (s:Student { name: 'Chris', age: 30 }), (l:Lesson { subject: 'Math', level: 99 })WITH s, lCREATE (s)-[r:`ENROLLED_IN`]->(l) RETURN r

What does that look like in Neo4jrb?student = Student.create(name: 'Chris', age: 30)

lesson = Lesson.create(subject: 'Math', level: 99)

student.lessons << lesson

OGM VS pure Cypher. Why? Part 2.There's also this…student.age = 31

student.save

Instead of…MATCH (s:Student) WHERE ID(s) = {your_id} SET s.age = 31 RETURN s

Or what about deleting?student.destroy

I'll take that over this:MATCH (s:Student)-[r]-() WHERE ID(s) = {your_id} DELETE s, r

What else? Easier Cypher Matches.

Neo4jrb provides what we think is the best Cypher DSL out there.

Return a student's lessons of level 99 taught by teachers aged 30.student.lessons(:l).where(level: 99).teachers.where(age: 30).pluck(:l)

Generates Cypher:"MATCH (student2305:`Student`), (l:`Lesson`), student2305-[rel0:`ENROLLED_IN`]->(l:`Lesson`), (result:`Teacher`), l<-[rel1:`lessons_taught`]-(result:`Teacher`) WHERE ID(student2305) = {ID_student2305} AND l.level = {l_level} AND result.age = {result_age} RETURN l | params: {:ID_student2305=>2305, :l_level=>99, :result_age=>30}"

That's cool, right?

And little shortcuts...Say your endpoint is /students/:student_id/lessons/:idand you want all of a given student's lessons in one query. The Cypher:"MATCH (s:Student)-[r:`ENROLLED_IN`]->(l:Lessons) WHERE s.uuid = {student_id} AND l.uuid = {lesson_id} RETURN l"

...you could write this, which requires some knowledge of the database:Student.where(uuid: params[:student_id).lessons.where(uuid: params[:id])

...but the OGM gives you this. You only need to know about your models:Student.all.match_to(params[:student_id]).lessons.match_to(params[:id])

Another thing: unique IDs by default.Problem: Neo Technology says not to count on its unique IDs for permalinks.

Solution: Neo4jrb generates a unique ID for each node out of the box.

This can be swapped out for the unique ID generation scheme of your choice.

We find a basic REST wrapper requires you to think about the database a lot.The goal here is to let you think more about your app, less about the database.

Getting started in RailsAdd to Gemfilegem 'neo4j'

Add to config/application.rbrequire 'neo4j/railtie'

Install Neo4jrake neo4j:install[community-2.1.5]

Omit from git if neededecho '/db/neo4j' >> .gitignore

Start Neo4jrake neo4j:start

See https://github.com/neo4jrb/neo4j/wiki/Neo4j-v3-Setup for more.

Anatomy of a node modelclass Student

include Neo4j::ActiveNode # adds all node methods

property :name

index :name

property :email, constraint: :unique

property :gpa, type: Integer

has_many :out, :lessons, rel_class: 'EnrolledIn'

end

● A label is created that matches the class name. Also creates finder methods, Student.find , Student.where, Student.find_by , etc,...

● A schema index is added to the :name property and a unique constraint is added to :email.● Declaring properties creates setters/getters, student.email /student.email=● has_many creates an association, allowing for Student.all.lessons.where , student.

lessons.each , etc,... rel_class tells it to look at the 'EnrolledIn' model for more info.

Anatomy of a rel modelclass EnrolledIn

include Neo4j::ActiveRel # adds all relationship methods

from_class Student

to_class Lesson

type 'ENROLLED_IN'

property :grade

property :created_at, type: DateTime

property :updated_at, type: DateTime

end

● Separates out relationship logic. Instead of student.lessons << lesson , do EnrolledIn.new(from_node: student, to_node: lesson).save

● You can add callbacks like after_create , all the standard Rails validations like validates_presence_of , custom properties with type conversion, custom methods, etc,...

● created_at/updated_at properties are set and updated automatically when declared.

Some tips...● Name ActiveRel models after relationship types. It follows Rails naming

conventions: EnrolledIn is the model, "ENROLLED_IN" is the type.● Not using ActivelRel? Use the 'type' option in ActiveNode associations.

The gem creates types for you but they're never ideal.● Neo4j makes it easy to perform dynamic calculations of things like "number

of comments on a post" and "number of students in a class" but this doesn't mean you always should. Counts like those don't change constantly, so consider storing them in properties on the node.

● Familiarize yourself with Cypher, play with Neo4j's web browser! You don't need to know Cypher to get started but to squeeze performance out of your app, you will eventually need to know it.

What's next?● Check out the sample app at https://github.com/subvertallchris/neodemo● Check out the gem at https://github.com/neo4jrb/neo4j● Get in touch with me: chris@subvertallmedia.com, @subvertallmedia on

twitter● Follow @neo4j and @neo4jrb on Twitter!● Tell your friends about Neo4j, the Ruby gem, and the NYC Neo4j Meetup!

www.meetup.com/nycneo4j/

Huge thanks to Nick Manning for organizing this event!

top related