NeoAccess library operations (incl. "NodeSpecs" objects)¶debug mode OFF)¶import os
import sys
import getpass
from neoaccess import NeoAccess
# In case of problems, try a sys.path.append(directory) , where directory is your project's root directory
NOTE: This tutorial is tested on version 4.4 of the Neo4j database, but will probably also work on the new version 5 (NOT guaranteed, however...)
# Save your credentials here - or use the prompts given by the next cell
host = "" # EXAMPLES: bolt://123.456.789.012 OR neo4j://localhost
# (CAUTION: do NOT include the port number!)
password = ""
db = NeoAccess(host=host,
credentials=("neo4j", password), debug=False) # Notice the debug option being OFF
Connection to Neo4j database established.
print("Version of the Neo4j driver: ", db.version())
Version of the Neo4j driver: 4.4.11
NeoAccess library operations¶# CLEAR OUT THE DATABASE
#db.empty_dbase() # UNCOMMENT IF DESIRED ***************** WARNING: USE WITH CAUTION!!! ************************
# 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'})
# 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
1

# Retrieve the car node (in the most straightforward way, using an internal database ID)
db.get_nodes(neo_car)
[{'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")
'white'
# How many owners does the car have?
db.count_links(neo_car, rel_name="OWNED_BY", rel_dir="OUT")
1
# Look up information about the car owner(s)
db.follow_links(neo_car, rel_name="OWNED_BY", rel_dir="OUT")
[{'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
<neoaccess.cypher_utils.NodeSpecs at 0x1a0dd790>
print(car_match) # Let's look at the specs we saved up; they will be used LATER in actual database operations
RAW match structure (object of class NodeSpecs):
internal_id: None labels: Car key_name: None key_value: None properties: {'color': 'white', 'make': 'Toyota'} clause: None clause_dummy_name: None
# 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)
RAW match structure (object of class NodeSpecs):
internal_id: None labels: Car key_name: None key_value: None properties: None clause: n.color = 'white' AND n.make = 'Toyota' clause_dummy_name: n
# 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
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
[{'color': 'white', 'make': 'Toyota'}]
db.get_nodes(car_match) # Fetch by "NodeSpecs" object returned by the match() method
[{'color': 'white', 'make': 'Toyota'}]
db.get_nodes(car_match_alt) # Fetch by an alternate version of the "NodeSpecs" object
[{'color': 'white', 'make': 'Toyota'}]
Likewise for the "Person" node:
db.get_nodes(neo_person) # Fetch by the internal database ID
[{'name': 'Julian'}]
db.get_nodes(person_match) # Fetch by "NodeSpecs" object returned by the match() method
[{'name': 'Julian'}]
db.get_nodes(person_match_alt_1)
[{'name': 'Julian'}]
db.get_nodes(person_match_alt_2)
[{'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)
[{'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
{'_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
[{'p.name': 'Julian', 'c.color': 'red', 'c.make': 'Toyota'}]