NeoAccess will become visible.¶NeoAccess library! General users should use the other tutorials.¶debug mode ON)¶import os
import sys
import getpass
from brainannex import GraphAccess
# In case of problems, try a sys.path.append(directory) , where directory is your project's root directory
# Save your credentials here - or use the prompts given by the next cell
host = "bolt://localhost" # EXAMPLES: bolt://123.456.789.012 OR bolt://localhost
# (CAUTION: do NOT include the port number!)
password = ""
db = GraphAccess(host=host,
credentials=("neo4j", password), debug=True) # Notice the debug option being ON
~~~~~~~~~ Initializing Intergraph object ~~~~~~~~~
Attempting to connect to Neo4j host 'bolt://localhost', with username 'neo4j'...
In query(). Query:
MATCH (n) RETURN n LIMIT 1
single_row: False , single_cell: `` , single_column : ``
Connection to host 'bolt://localhost' established. *** IN DEBUG MODE ***
print("Version of the Neo4j driver: ", db.version())
Version of the Neo4j driver: 4.4.13
GraphAccess library operations¶# CLEAR OUT THE DATABASE
#db.empty_dbase() # UNCOMMENT IF DESIRED ***************** WARNING: USE WITH CAUTION!!! ************************
In query(). Query:
MATCH (n) DETACH DELETE(n)
single_row: False , single_cell: `` , single_column : ``
# Create a "Car" node and a "Person" node
neo_car = db.create_node("Car", {'color': 'white', 'make': 'Toyota'}) # create_node returns the internal database ID of the new node
neo_person = db.create_node("Person", {'name': 'Julian'})
In query_extended(). Query:
CREATE (n :`Car` {`color`: $par_1, `make`: $par_2}) RETURN n
Data binding:
{'par_1': 'white', 'par_2': 'Toyota'}
flatten: True , fields_to_exclude: None
In query_extended(). Query:
CREATE (n :`Person` {`name`: $par_1}) RETURN n
Data binding:
{'par_1': 'Julian'}
flatten: True , fields_to_exclude: None
# Link the "Car" node to the "Person" node (using internal database ID's to refer to existing nodes)
number_added = db.add_links(match_from=neo_car, match_to=neo_person, rel_name="OWNED_BY")
number_added
In update_query(). Query:
MATCH (from), (to)
WHERE (id(from) = 3470 AND id(to) = 3471)
MERGE (from) -[:`OWNED_BY`]-> (to)
In update_query(). Attributes of ResultSummary object:
metadata -> {'query': '\n MATCH (from), (to)\n WHERE (id(from) = 3470 AND id(to) = 3471)\n MERGE (from) -[:`OWNED_BY`]-> (to) \n ', 'parameters': {}, 'server': <neo4j.api.ServerInfo object at 0x0000020B7E77FD10>, 't_first': 15, 'fields': [], 'bookmark': 'FB:kcwQvb2+baJ+TrKHXmXfUdIVwsoAB6BikA==', 'stats': {'contains-updates': True, 'relationships-created': 1}, 'type': 'w', 't_last': 0, 'db': 'neo4j', 'notifications': [{'severity': 'WARNING', 'description': 'If a part of a query contains multiple disconnected patterns, this will build a cartesian product between all those parts. This may produce a large amount of data and slow down query processing. While occasionally intended, it may often be possible to reformulate the query that avoids the use of this cross product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH (identifier is: (to))', 'code': 'Neo.ClientNotification.Statement.CartesianProductWarning', 'position': {'column': 13, 'offset': 13, 'line': 2}, 'title': 'This query builds a cartesian product between disconnected patterns.'}]}
server -> <neo4j.api.ServerInfo object at 0x0000020B7E77FD10>
database -> neo4j
query ->
MATCH (from), (to)
WHERE (id(from) = 3470 AND id(to) = 3471)
MERGE (from) -[:`OWNED_BY`]-> (to)
parameters -> {}
query_type -> w
plan -> None
profile -> None
notifications -> [{'severity': 'WARNING', 'description': 'If a part of a query contains multiple disconnected patterns, this will build a cartesian product between all those parts. This may produce a large amount of data and slow down query processing. While occasionally intended, it may often be possible to reformulate the query that avoids the use of this cross product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH (identifier is: (to))', 'code': 'Neo.ClientNotification.Statement.CartesianProductWarning', 'position': {'column': 13, 'offset': 13, 'line': 2}, 'title': 'This query builds a cartesian product between disconnected patterns.'}]
counters -> {'_contains_updates': True, 'relationships_created': 1}
result_available_after -> 15
result_consumed_after -> 0
1

# Retrieve the car node (in the most straightforward way, using an internal database ID)
db.get_nodes(neo_car)
In query_extended(). Query:
MATCH (n) WHERE (id(n) = 3470) RETURN n
flatten: True , fields_to_exclude: ['internal_id', '_node_labels']
[{'color': 'white', 'make': 'Toyota'}]
# Retrieve a single property of the car node (to be used when only 1 node is present)
db.get_nodes(neo_car, single_cell="color")
In query_extended(). Query:
MATCH (n) WHERE (id(n) = 3470) RETURN n
flatten: True , fields_to_exclude: ['internal_id', '_node_labels']
'white'
# How many owners does the car have?
db.count_links(neo_car, rel_name="OWNED_BY", rel_dir="OUT")
In query(). Query:
MATCH (n) - [:OWNED_BY] -> (neighbor )WHERE (id(n) = 3470) RETURN count(neighbor) AS link_count
single_row: False , single_cell: `link_count` , single_column : ``
1
# Look up information about the car owner(s)
db.follow_links(neo_car, rel_name="OWNED_BY", rel_dir="OUT")
In query(). Query:
MATCH (n) - [:OWNED_BY] -> (neighbor )WHERE (id(n) = 3470) RETURN neighbor LIMIT 100
single_row: False , single_cell: `` , single_column : ``
[{'name': 'Julian'}]
# Lets provide a way to later look up the "Car" node, using the match() method.
# IMPORTANT: NO DATABASE OPERATION IS ACTUALLY PERFORMED HERE! We're just saving up all the specs
# (to indentify a node, OR GROUP OF NODES) into an object of class "NodeSpecs"
car_match = db.match(labels="Car", properties={'color': 'white', 'make': 'Toyota'})
car_match
<brainannex.cypher_utils.CypherBuilder at 0x20b7fa710d0>
print(car_match) # Let's look at the specs we saved up; they will be used LATER in actual database operations
Object properties:
internal_id: None
labels: Car
key_name: None
key_value: None
properties: {'color': 'white', 'make': 'Toyota'}
clause:
clause_binding: {}
dummy_node_name: n
node: (n :`Car` {`color`: $n_par_1, `make`: $n_par_2})
where:
data_binding: {'n_par_1': 'white', 'n_par_2': 'Toyota'}
cypher: MATCH (n :`Car` {`color`: $n_par_1, `make`: $n_par_2})
# A lot of parameters can be passed to match(). Some examples of alternative ways to specify the same node as above:
car_match_alt = db.match(labels="Car", clause="n.color = 'white' AND n.make = 'Toyota'", dummy_node_name="n")
print(car_match_alt)
Object properties:
internal_id: None
labels: Car
key_name: None
key_value: None
properties: {}
clause: n.color = 'white' AND n.make = 'Toyota'
clause_binding: {}
dummy_node_name: n
node: (n :`Car` )
where: n.color = 'white' AND n.make = 'Toyota'
data_binding: {}
cypher: MATCH (n :`Car` ) WHERE (n.color = 'white' AND n.make = 'Toyota')
# Various ways to specify our Person node (again, NO DATABASE OPERATION IS ACTUALLY PERFORMED HERE!)
person_match = db.match(labels="Person", properties={'name': 'Julian'})
person_match_alt_1 = db.match(labels="Person", clause="n.name = 'Julian'", dummy_node_name="n")
person_match_alt_2 = db.match(labels="Person", key_name='name', key_value='Julian')
Note: NO EXTRA DATABASE OPERATIONS ARE WASTED ON LOCATING THE NODES! Efficient, 1-step, database queries are created and executed at the very LAST stage; for example to create the following link
# Link the "Person" node to the "Car" node (a reverse link of the one we created before)
# HERE'S WHERE THE (SINGLE) DATABASE OPERATION ACTUALLY GETS PERFORMED
number_added = db.add_links(match_from=person_match, match_to=car_match, rel_name="OWNS")
number_added
In update_query(). Query:
MATCH (from :`Person` {`name`: $from_par_1}), (to :`Car` {`color`: $to_par_1, `make`: $to_par_2})
MERGE (from) -[:`OWNS`]-> (to)
Data binding:
{'from_par_1': 'Julian', 'to_par_1': 'white', 'to_par_2': 'Toyota'}
In update_query(). Attributes of ResultSummary object:
metadata -> {'query': '\n MATCH (from :`Person` {`name`: $from_par_1}), (to :`Car` {`color`: $to_par_1, `make`: $to_par_2})\n \n MERGE (from) -[:`OWNS`]-> (to) \n ', 'parameters': {'from_par_1': 'Julian', 'to_par_1': 'white', 'to_par_2': 'Toyota'}, 'server': <neo4j.api.ServerInfo object at 0x0000020B7E77FD10>, 't_first': 32, 'fields': [], 'bookmark': 'FB:kcwQvb2+baJ+TrKHXmXfUdIVwsoAB6BjkA==', 'stats': {'contains-updates': True, 'relationships-created': 1}, 'type': 'w', 't_last': 0, 'db': 'neo4j', 'notifications': [{'severity': 'WARNING', 'description': 'If a part of a query contains multiple disconnected patterns, this will build a cartesian product between all those parts. This may produce a large amount of data and slow down query processing. While occasionally intended, it may often be possible to reformulate the query that avoids the use of this cross product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH (identifier is: (to))', 'code': 'Neo.ClientNotification.Statement.CartesianProductWarning', 'position': {'column': 13, 'offset': 13, 'line': 2}, 'title': 'This query builds a cartesian product between disconnected patterns.'}]}
server -> <neo4j.api.ServerInfo object at 0x0000020B7E77FD10>
database -> neo4j
query ->
MATCH (from :`Person` {`name`: $from_par_1}), (to :`Car` {`color`: $to_par_1, `make`: $to_par_2})
MERGE (from) -[:`OWNS`]-> (to)
parameters -> {'from_par_1': 'Julian', 'to_par_1': 'white', 'to_par_2': 'Toyota'}
query_type -> w
plan -> None
profile -> None
notifications -> [{'severity': 'WARNING', 'description': 'If a part of a query contains multiple disconnected patterns, this will build a cartesian product between all those parts. This may produce a large amount of data and slow down query processing. While occasionally intended, it may often be possible to reformulate the query that avoids the use of this cross product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH (identifier is: (to))', 'code': 'Neo.ClientNotification.Statement.CartesianProductWarning', 'position': {'column': 13, 'offset': 13, 'line': 2}, 'title': 'This query builds a cartesian product between disconnected patterns.'}]
counters -> {'_contains_updates': True, 'relationships_created': 1}
result_available_after -> 32
result_consumed_after -> 0
1

The "Car" node can be found and extracted (performing a DATABASE OPERATION), using EITHER its Internal Database ID (which we had saved at the very beginning, though we we were acting like we didn't) OR any of the alternative ways we created to specify it
db.get_nodes(neo_car) # Fetch by the internal database ID
In query_extended(). Query:
MATCH (n) WHERE (id(n) = 3470) RETURN n
flatten: True , fields_to_exclude: ['internal_id', '_node_labels']
[{'color': 'white', 'make': 'Toyota'}]
db.get_nodes(car_match) # Fetch by "NodeSpecs" object returned by the match() method
In query_extended(). Query:
MATCH (n :`Car` {`color`: $n_par_1, `make`: $n_par_2}) RETURN n
Data binding:
{'n_par_1': 'white', 'n_par_2': 'Toyota'}
flatten: True , fields_to_exclude: ['internal_id', '_node_labels']
[{'color': 'white', 'make': 'Toyota'}]
db.get_nodes(car_match_alt) # Fetch by an alternate version of the "NodeSpecs" object
In query_extended(). Query:
MATCH (n :`Car` ) WHERE (n.color = 'white' AND n.make = 'Toyota') RETURN n
flatten: True , fields_to_exclude: ['internal_id', '_node_labels']
[{'color': 'white', 'make': 'Toyota'}]
Likewise for the "Person" node:
db.get_nodes(neo_person) # Fetch by the internal database ID
In query_extended(). Query:
MATCH (n) WHERE (id(n) = 3471) RETURN n
flatten: True , fields_to_exclude: ['internal_id', '_node_labels']
[{'name': 'Julian'}]
db.get_nodes(person_match) # Fetch by "NodeSpecs" object returned by the match() method
In query_extended(). Query:
MATCH (n :`Person` {`name`: $n_par_1}) RETURN n
Data binding:
{'n_par_1': 'Julian'}
flatten: True , fields_to_exclude: ['internal_id', '_node_labels']
[{'name': 'Julian'}]
db.get_nodes(person_match_alt_1)
In query_extended(). Query:
MATCH (n :`Person` ) WHERE (n.name = 'Julian') RETURN n
flatten: True , fields_to_exclude: ['internal_id', '_node_labels']
[{'name': 'Julian'}]
db.get_nodes(person_match_alt_2)
In query_extended(). Query:
MATCH (n :`Person` {`name`: $n_par_1}) RETURN n
Data binding:
{'n_par_1': 'Julian'}
flatten: True , fields_to_exclude: ['internal_id', '_node_labels']
[{'name': 'Julian'}]
q = '''MATCH (p :Person) -[:OWNS] -> (c :Car) -[OWNED_BY] -> (p)
RETURN p.name, c.color, c.make
''' # This query will verify the forward and reverse links that we created earlier
db.query(q) # Run the query; by default, it will return a list of records (each record is a dict)
In query(). Query:
MATCH (p :Person) -[:OWNS] -> (c :Car) -[OWNED_BY] -> (p)
RETURN p.name, c.color, c.make
single_row: False , single_cell: `` , single_column : ``
[{'p.name': 'Julian', 'c.color': 'white', 'c.make': 'Toyota'}]
q_paint_car_red = '''MATCH (c :Car) -[OWNED_BY] -> (p :Person {name: 'Julian'})
SET c.color = 'red'
''' # Paint all of Julian's cars red!
result = db.update_query(q_paint_car_red) # It returns a dict of info about what it did
result
In update_query(). Query:
MATCH (c :Car) -[OWNED_BY] -> (p :Person {name: 'Julian'})
SET c.color = 'red'
In update_query(). Attributes of ResultSummary object:
metadata -> {'query': "MATCH (c :Car) -[OWNED_BY] -> (p :Person {name: 'Julian'})\n SET c.color = 'red'\n ", 'parameters': {}, 'server': <neo4j.api.ServerInfo object at 0x0000020B7E77FD10>, 't_first': 18, 'fields': [], 'bookmark': 'FB:kcwQvb2+baJ+TrKHXmXfUdIVwsoAB6BkkA==', 'stats': {'contains-updates': True, 'properties-set': 1}, 'type': 'w', 't_last': 0, 'db': 'neo4j'}
server -> <neo4j.api.ServerInfo object at 0x0000020B7E77FD10>
database -> neo4j
query -> MATCH (c :Car) -[OWNED_BY] -> (p :Person {name: 'Julian'})
SET c.color = 'red'
parameters -> {}
query_type -> w
plan -> None
profile -> None
notifications -> None
counters -> {'_contains_updates': True, 'properties_set': 1}
result_available_after -> 18
result_consumed_after -> 0
{'_contains_updates': True, 'properties_set': 1, 'returned_data': []}
db.query(q) # Re-run the earlier query (to verify the forward and reverse links); notice how the car is now red
In query(). Query:
MATCH (p :Person) -[:OWNS] -> (c :Car) -[OWNED_BY] -> (p)
RETURN p.name, c.color, c.make
single_row: False , single_cell: `` , single_column : ``
[{'p.name': 'Julian', 'c.color': 'red', 'c.make': 'Toyota'}]