49
Introducing py2neo [email protected] py2neo.org @technige @py2neo

Introduction to py2neo

Embed Size (px)

DESCRIPTION

An introduction to the python library py2neo, used for building applications which use the Neo4j graph database.

Citation preview

Page 1: Introduction to py2neo

Introducing

py2neo

[email protected] py2neo.org@technige @py2neo

Page 2: Introduction to py2neo

Me...

Page 3: Introduction to py2neo

Where does py2neo fit in?

Neo4j

REST Server

(your app)

py2neo

GET /db/data/200 OK

Page 4: Introduction to py2neo

Coming Up...

● Connecting & Creating● Property Containers● Indexes & Uniqueness● Cypher & Geoff● Installation● Future Plans

Page 5: Introduction to py2neo

Connecting & Creating

Page 6: Introduction to py2neo

Default Connections

>>> from py2neo import neo4j

>>> graph_db = neo4j.GraphDatabaseService()

default connection is made to <http://localhost:7474/db/data/>

Page 7: Introduction to py2neo

Custom Connections

>>> from py2neo import neo4j

>>> uri = "http://otherserv:9999/db/data/"

>>> graph_db = neo4j.GraphDatabaseService(uri)

>>> graph_db.neo4j_version

(1, 8, u'M04', 1, u'g892e348')

Page 8: Introduction to py2neo

A few simple methods...

>>> graph_db.get_node(0)

Node('http://localhost:7474/db/data/node/0')

>>> graph_db.get_reference_node()

Node('http://localhost:7474/db/data/node/0')

>>> graph_db.get_node_count()

1

>>> graph_db.get_relationship_count()

0

Page 9: Introduction to py2neo

Sample data: a family tree

All characters appearing in this work are fictitious. Any resemblance to real persons, living or dead, is purely coincidental.

Phil (1921) Liz (1926)

Anne (1950)Chaz (1948) Andy (1960) Ed (1964)

m(1947)

Page 10: Introduction to py2neo

create

nodes = graph_db.create(

{"name": "Phil", "born": 1921},

{"name": "Liz", "born": 1926},

{"name": "Chaz", "born": 1948},

{"name": "Anne", "born": 1950},

{"name": "Andy", "born": 1960},

{"name": "Ed", "born": 1964},

)

Phil Liz

Chaz Anne

Andy Ed

list of nodes

Page 11: Introduction to py2neo

create

rels = graph_db.create(

(phil, "MARRIED", liz),

(chaz, "FATHER", phil),

(chaz, "MOTHER", liz),

)

Phil Liz

Chaz

MARRIED

MOTHER FATHER

node must already exist

list of relationships

Page 12: Introduction to py2neo

create

family = graph_db.create(

{"name": "Phil", "born": 1921}, {"name": "Liz", "born": 1926},

{"name": "Chaz", "born": 1948}, {"name": "Anne", "born": 1950},

{"name": "Andy", "born": 1960}, {"name": "Ed", "born": 1964},

(0, "MARRIED", 1, {"year": 1947, "place": "London"}),

(2, "FATHER", 0), (3, "FATHER", 0),

(4, "FATHER", 0), (5, "FATHER", 0),

(2, "MOTHER", 1), (3, "MOTHER", 1),

(4, "MOTHER", 1), (5, "MOTHER", 1),

)

Page 13: Introduction to py2neo

create

family = graph_db.create(

{"name": "Phil", "born": 1921}, {"name": "Liz", "born": 1926},

{"name": "Chaz", "born": 1948}, {"name": "Anne", "born": 1950},

{"name": "Andy", "born": 1960}, {"name": "Ed", "born": 1964},

(0, "MARRIED", 1, {"year": 1947, "place": "London"}),

(2, "FATHER", 0), (3, "FATHER", 0),

(4, "FATHER", 0), (5, "FATHER", 0),

(2, "MOTHER", 1), (3, "MOTHER", 1),

(4, "MOTHER", 1), (5, "MOTHER", 1),

)

nodes, rels = family[0:6], family[6:]

phil, liz, chaz, anne, andy, ed = nodes

list of nodes and relationships

node defined in same batch

relationship properties

Page 14: Introduction to py2neo

Property Containers

Page 15: Introduction to py2neo

neo4j.PropertyContainer

Node Relationship

PropertyContainer

Page 16: Introduction to py2neo

PropertyContainers implement many of the

container methods defined by the Python standard

<http://docs.python.org/reference/datamodel.html#emulating-container-types>

Page 17: Introduction to py2neo

neo4j.PropertyContainer

# update properties

liz["star_sign"] = "Taurus"

# query properties

for node in nodes:

name = node["name"]

if "star_sign" in node:

print name + " is a node["star_sign"]

else:

print name + " doesn't believe in horoscopes"

set property

get property

test property containment

Page 18: Introduction to py2neo

neo4j.Node

Node Relationship

PropertyContainer

Page 19: Introduction to py2neo

Relationships and Related Nodes

parental_rels = liz.get_relationships(

neo4j.Direction.INCOMING, "MOTHER"

)

parental_rels = liz.get_relationships_with(

chaz, neo4j.Direction.INCOMING, "MOTHER"

)

children = liz.get_related_nodes(

neo4j.Direction.INCOMING, "MOTHER"

)

Liz

Chaz

Anne

Liz

Chaz

Anne

Liz

Chaz

Anne

Page 20: Introduction to py2neo

Relationships and Related Nodes

>>> liz.has_relationship(neo4j.Direction.BOTH, "MARRIED")

True

>>> anne.is_related_to(phil, neo4j.Direction.OUTGOING, "FATHER")

True

>>> ed.is_related_to(andy, neo4j.Direction.BOTH, "BROTHER")

False

could also specify multiple types

Page 21: Introduction to py2neo

neo4j.Relationship

Node Relationship

PropertyContainer

Page 22: Introduction to py2neo

neo4j.Relationship

>>> rels[0].start_node

Node('http://localhost:7474/db/data/node/1')

>>> rels[0].end_node

Node('http://localhost:7474/db/data/node/2')

>>> rels[0].type

'MARRIED'

Page 23: Introduction to py2neo

More sample data: a second tree

George (1895) Betty (1900)

Liz (1926) Maggie (1930)

m

Page 24: Introduction to py2neo

Phil (1921) Liz (1926)

Anne (1950)Chaz (1948) Andy (1960) Ed (1964)

m(1947)

George (1895) Betty (1900)

Liz (1926) Maggie (1930)

m

Page 25: Introduction to py2neo

Phil (1921) Liz (1926)

Anne (1950)Chaz (1948) Andy (1960) Ed (1964)

m(1947)

George (1895) Betty (1900)

Liz (1926) Maggie (1930)

m

same person

Page 26: Introduction to py2neo

How can we avoid duplicate nodes?

Page 27: Introduction to py2neo

Indexes

Page 28: Introduction to py2neo

Indexing the first family...

>>> people = graph_db.get_or_create_index(neo4j.Node, "People")

>>> for node in nodes:

... people.add("name", node["name"], node)

>>> people.get("name", "Liz")

[Node('http://localhost:7474/db/data/node/2')]

list of matching entities

Page 29: Introduction to py2neo

...and the second

>>> new_props = [

... {"name": "George", "born": 1895},

... {"name": "Betty", "born": 1900},

... {"name": "Liz", "born": 1926},

... {"name": "Maggie", "born": 1930},

... ]

>>> george, betty, liz, maggie = [

... people.get_or_create("name", prop["name"], prop)

... for prop in new_props

... ]

>>> people.get("name", "Liz")

[Node('http://localhost:7474/db/data/node/2')]

same node as before

Page 30: Introduction to py2neo

People

name Andy

name Anne

name Betty

name Chaz

name Ed

name George

name Liz

name Maggie

name Phil

A Quick Query

>>> people.query("name:*e*")

[Node('http://localhost:7474/db/data/node/4'),

Node('http://localhost:7474/db/data/node/7'),

Node('http://localhost:7474/db/data/node/8'),

Node('http://localhost:7474/db/data/node/9')]

Maggie

Anne

Betty

George

Page 31: Introduction to py2neo

We've added the nodes... what about the relationships?

Page 32: Introduction to py2neo

Cypher & Geoff

Page 33: Introduction to py2neo

Cypher RELATE

START a=node(1), b=node(2)

RELATE (a)-[ab:KNOWS]->(b)

RETURN ab

Page 34: Introduction to py2neo

Cypher RELATE

a b RELATE a b

a b RELATE a b

a b RELATE !

Page 35: Introduction to py2neo

relate

new_rels = graph_db.relate(

(george, "MARRIED", betty),

(liz, "FATHER", george),

(maggie, "FATHER", george),

(liz, "MOTHER", betty),

(maggie, "MOTHER", betty),

)George Betty

Maggie

MARRIED

MOTHERFATHER

Liz

FATHER MOTHER

Page 36: Introduction to py2neo

For relationships,

relate can be seen as an idempotent

alternative to create

Page 37: Introduction to py2neo

cypher.execute

>>> from py2neo import cypher

>>> query = "START q=node(1) RETURN q"

>>> data, metadata = cypher.execute(graph_db, query)

>>> for row in data:

... q = row[0]

... print q

first column

Page 38: Introduction to py2neo

cypher.execute

>>> from py2neo import cypher

>>> query = "START q=node(1) RETURN q"

>>> data, metadata = cypher.execute(graph_db, query)

>>> for row in data:

... q = row[0]

... print q

available only after all rows received

first column

Page 39: Introduction to py2neo

cypher.execute

query = "START q=node(1) RETURN q"

def print_row(row):

q = row[0]

print row

cypher.execute(graph_db, query, row_handler=print_row)

executed once per row as each row is received

Page 40: Introduction to py2neo

Command Line Cypherelgin@forge:~% cypher "start a=node(1) match (a)-[:MARRIED]->(b) return a, b"

+------------------------------------------------------------------+

| a | b |

+------------------------------------------------------------------+

| (1) {"name":"Phil","born":1921} | (2) {"name":"Liz","born":1926} |

+------------------------------------------------------------------+

elgin@forge:~% cypher -f csv "start a=node(1) match (a)-[ab:MARRIED]->(b) return a, ab, b"

"a","ab","b"

"(1)","(1)-[0:MARRIED]->(2)","(2)"

elgin@forge:~% cypher -f json "start a=node(1) match (a)-[ab:MARRIED]->(b) return a, ab, b"

[

{"a": "(1)", "ab": "(1)-[0:MARRIED]->(2)", "b": "(2)"}

]

elgin@forge:~% cypher -f geoff "start a=node(1) match (a)-[ab:MARRIED]->(b) return a, ab, b"

(1) {"name": "Phil", "born": 1921}

(2) {"name": "Liz", "born": 1926}

(1)-[0:MARRIED]->(2) {"year": 1947, "place": "London"}

Page 41: Introduction to py2neo

Geoff is to graph dataas

CSV is to tabular data

<http://geoff.nigelsmall.net/>

Page 42: Introduction to py2neo

elgin@forge:~% cypher -f geoff "start n=node(*), r=rel(*) return n, r"

(0) {}

(1) {"born": 1921, "name": "Phil", "family": "Windsor"}

(2) {"born": 1926, "star_sign": "Taurus", "family": "Windsor", "name": "Liz"}

(3) {"born": 1948, "name": "Chaz", "family": "Windsor"}

(4) {"born": 1950, "name": "Anne", "family": "Windsor"}

(5) {"born": 1960, "name": "Andy", "family": "Windsor"}

(6) {"born": 1964, "name": "Ed", "family": "Windsor"}

(7) {"born": 1895, "name": "George"}

(8) {"born": 1900, "name": "Betty"}

(9) {"born": 1930, "name": "Maggie"}

(1)-[0:MARRIED]->(2) {"place": "London", "year": 1947}

(3)-[1:FATHER]->(1) {}

(4)-[2:FATHER]->(1) {}

(5)-[3:FATHER]->(1) {}

(6)-[4:FATHER]->(1) {}

(3)-[5:MOTHER]->(2) {}

(4)-[6:MOTHER]->(2) {}

(5)-[7:MOTHER]->(2) {}

(6)-[8:MOTHER]->(2) {}

(7)-[9:MARRIED]->(8) {}

(2)-[10:FATHER]->(7) {}

(9)-[11:FATHER]->(7) {}

(2)-[12:MOTHER]->(8) {}

(9)-[13:MOTHER]->(8) {}

Page 43: Introduction to py2neo

Neo4j Console

Page 44: Introduction to py2neo

Installation

Page 45: Introduction to py2neo

Requirements

● Python 2.6+ <http://python.org/>● Tornado 2.2.1 <http://www.tornadoweb.org/>● Neo4j 1.6+ <http://neo4j.org/>

Page 46: Introduction to py2neo

Installation

<http://pypi.python.org/pypi/py2neo>

elgin@forge:~% sudo pip install py2neo

elgin@forge:~% sudo pip install --upgrade py2neo

Page 47: Introduction to py2neo

Source Code

<https://github.com/nigelsmall/py2neo>

elgin@forge:~% git clone [email protected]:nigelsmall/py2neo.git

Page 48: Introduction to py2neo

Future Plans

● Fast HTTP● Multi-threading support● Python 3● Command line tools● Property caching● Test harness for multiple Neo4j versions● New methods, e.g. get_paths_to

Page 49: Introduction to py2neo

EOF

[email protected] py2neo.org@technige @py2neo