Upload
nigel-small
View
11.434
Download
1
Tags:
Embed Size (px)
DESCRIPTION
An introduction to the python library py2neo, used for building applications which use the Neo4j graph database.
Citation preview
Me...
Where does py2neo fit in?
Neo4j
REST Server
(your app)
py2neo
GET /db/data/200 OK
Coming Up...
● Connecting & Creating● Property Containers● Indexes & Uniqueness● Cypher & Geoff● Installation● Future Plans
Connecting & Creating
Default Connections
>>> from py2neo import neo4j
>>> graph_db = neo4j.GraphDatabaseService()
default connection is made to <http://localhost:7474/db/data/>
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')
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
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)
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
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
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),
)
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
Property Containers
neo4j.PropertyContainer
Node Relationship
PropertyContainer
PropertyContainers implement many of the
container methods defined by the Python standard
<http://docs.python.org/reference/datamodel.html#emulating-container-types>
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
neo4j.Node
Node Relationship
PropertyContainer
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
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
neo4j.Relationship
Node Relationship
PropertyContainer
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'
More sample data: a second tree
George (1895) Betty (1900)
Liz (1926) Maggie (1930)
m
Phil (1921) Liz (1926)
Anne (1950)Chaz (1948) Andy (1960) Ed (1964)
m(1947)
George (1895) Betty (1900)
Liz (1926) Maggie (1930)
m
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
How can we avoid duplicate nodes?
Indexes
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
...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
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
We've added the nodes... what about the relationships?
Cypher & Geoff
Cypher RELATE
START a=node(1), b=node(2)
RELATE (a)-[ab:KNOWS]->(b)
RETURN ab
Cypher RELATE
a b RELATE a b
a b RELATE a b
a b RELATE !
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
For relationships,
relate can be seen as an idempotent
alternative to create
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
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
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
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"}
Geoff is to graph dataas
CSV is to tabular data
<http://geoff.nigelsmall.net/>
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) {}
Neo4j Console
Installation
Requirements
● Python 2.6+ <http://python.org/>● Tornado 2.2.1 <http://www.tornadoweb.org/>● Neo4j 1.6+ <http://neo4j.org/>
Installation
<http://pypi.python.org/pypi/py2neo>
elgin@forge:~% sudo pip install py2neo
elgin@forge:~% sudo pip install --upgrade py2neo
Source Code
<https://github.com/nigelsmall/py2neo>
elgin@forge:~% git clone [email protected]:nigelsmall/py2neo.git
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
EOF
[email protected] py2neo.org@technige @py2neo