This is an automated email from the ASF dual-hosted git repository.
rafsun42 pushed a commit to branch PG14
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/PG14 by this push:
new 12fb6af9 Fix json serialization error in Python Driver (#1228) (#1595)
12fb6af9 is described below
commit 12fb6af972f0a5b158fe359e7a57f3c9322d58fb
Author: John Gemignani <[email protected]>
AuthorDate: Wed Feb 14 10:23:09 2024 -0800
Fix json serialization error in Python Driver (#1228) (#1595)
* * Added test to catch the error in toJson()
* removed the trailing comma in _nodeToJson() node.properties part
* removed the trailing comma in _nodeToString() node.properties part
* * Added missing ", ".join() _nodeToString() in node.properties part
---------
Co-authored-by: Olivier <[email protected]>
Resolved -
Conflicts:
drivers/python/test_age_py.py
Co-authored-by: omaurel-socha
<[email protected]>
---
drivers/python/age/models.py | 24 +-
drivers/python/test_age_py.py | 782 ++++++++++++++++++++++--------------------
2 files changed, 428 insertions(+), 378 deletions(-)
diff --git a/drivers/python/age/models.py b/drivers/python/age/models.py
index 838110a1..aee1b759 100644
--- a/drivers/python/age/models.py
+++ b/drivers/python/age/models.py
@@ -238,11 +238,12 @@ def _nodeToString(node, buf, extraFormatter=None):
if node.properties != None:
buf.write(", properties:{")
- for k,v in node.properties.items():
- buf.write(k)
- buf.write(": ")
- buf.write(str(v))
- buf.write(", ")
+ prop_list = []
+ for k, v in node.properties.items():
+ prop_list.append(f"{k}: {str(v)}")
+
+ # Join properties with comma and write to buffer
+ buf.write(", ".join(prop_list))
buf.write("}")
if extraFormatter != None:
@@ -281,12 +282,13 @@ def _nodeToJson(node, buf, extraFormatter=None):
if node.properties != None:
buf.write(", \"properties\":{")
- for k,v in node.properties.items():
- buf.write("\"")
- buf.write(k)
- buf.write("\": \"")
- buf.write(str(v))
- buf.write("\", ")
+
+ prop_list = []
+ for k, v in node.properties.items():
+ prop_list.append(f"\"{k}\": \"{str(v)}\"")
+
+ # Join properties with comma and write to buffer
+ buf.write(", ".join(prop_list))
buf.write("}")
buf.write("}")
\ No newline at end of file
diff --git a/drivers/python/test_age_py.py b/drivers/python/test_age_py.py
index 99c8d085..1161fda8 100644
--- a/drivers/python/test_age_py.py
+++ b/drivers/python/test_age_py.py
@@ -1,368 +1,416 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-# http://www.apache.org/licenses/LICENSE-2.0
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-from age.models import Vertex
-import unittest
-import decimal
-import age
-import argparse
-
-DSN = "host=127.0.0.1 port=5432 dbname=postgres user=postgres password=agens"
-
-class TestAgeBasic(unittest.TestCase):
- ag = None
- def setUp(self):
- print("Connecting to Test Graph.....")
- TEST_DB = self.args.database
- TEST_USER = self.args.user
- TEST_PASSWORD = self.args.password
- TEST_PORT = self.args.port
- TEST_HOST = self.args.host
- TEST_GRAPH_NAME = self.args.graphName
- self.ag = age.connect(graph=TEST_GRAPH_NAME, host=TEST_HOST,
port=TEST_PORT, dbname=TEST_DB, user=TEST_USER, password=TEST_PASSWORD)
-
-
- def tearDown(self):
- # Clear test data
- print("Deleting Test Graph.....")
- age.deleteGraph(self.ag.connection, self.ag.graphName)
- self.ag.close()
-
- def testExec(self):
-
- print("\n---------------------------------------------------")
- print("Test 1: Checking single and multi column Returns.....")
- print("---------------------------------------------------\n")
-
- ag = self.ag
- # Create and Return single column
- cursor = ag.execCypher("CREATE (n:Person {name: %s, title:
'Developer'}) RETURN n", params=('Andy',))
- for row in cursor:
- print("Vertex: %s , Type: %s " % (Vertex, type(row[0])))
-
-
- # Create and Return multi columns
- cursor = ag.execCypher("CREATE (n:Person {name: %s, title: %s}) RETURN
id(n), n.name", cols=['id','name'], params=('Jack','Manager'))
- row = cursor.fetchone()
- print("Id: %s , Name: %s" % (row[0], row[1]))
- self.assertEqual(int, type(row[0]))
- ag.commit()
- print("\nTest 1 Successful....")
-
-
-
-
- def testQuery(self):
-
- print("\n--------------------------------------------------")
- print("Test 2: Testing CREATE and query relationships....." )
- print("--------------------------------------------------\n")
-
- ag = self.ag
- ag.execCypher("CREATE (n:Person {name: %s}) ", params=('Jack',))
- ag.execCypher("CREATE (n:Person {name: %s}) ", params=('Andy',))
- ag.execCypher("CREATE (n:Person {name: %s}) ", params=('Smith',))
- ag.execCypher("MATCH (a:Person), (b:Person) WHERE a.name = 'Andy' AND
b.name = 'Jack' CREATE (a)-[r:worksWith {weight: 3}]->(b)")
- ag.execCypher("""MATCH (a:Person), (b:Person)
- WHERE a.name = %s AND b.name = %s
- CREATE p=((a)-[r:worksWith]->(b)) """, params=('Jack',
'Smith',))
-
- ag.commit()
-
- cursor = ag.execCypher("MATCH p=()-[:worksWith]-() RETURN p")
- for row in cursor:
- path = row[0]
- print("START:", path[0])
- print("EDGE:", path[1])
- print("END:", path[2])
-
- cursor = ag.execCypher("MATCH p=(a)-[b]-(c) WHERE b.weight>2 RETURN
a,label(b), b.weight, c", cols=["a","bl","bw", "c"], params=(2,))
- for row in cursor:
- start = row[0]
- edgel = row[1]
- edgew = row[2]
- end = row[3]
- print("Relationship: %s %s %s. Edge weight: %s" % (start["name"] ,
edgel,end["name"], edgew))
- #Assert that the weight of the edge is greater than 2
- self.assertEqual(edgew > 2, True)
- print("\nTest 2 Successful...")
-
-
- def testChangeData(self):
-
- print("\n-------------------------------------------------------")
- print("Test 3: Testing changes in data using SET and REMOVE.....")
- print("-------------------------------------------------------\n")
-
- ag = self.ag
- # Create Vertices
- # Commit automatically
- ag.execCypher("CREATE (n:Person {name: 'Joe'})")
-
- cursor = ag.execCypher("CREATE (n:Person {name: %s, title:
'Developer'}) RETURN n", params=('Smith',))
- row = cursor.fetchone()
- print("CREATED: ", row[0])
-
- # You must commit explicitly
- ag.commit()
-
- cursor = ag.execCypher("MATCH (n:Person {name: %s}) SET n.title=%s
RETURN n", params=('Smith','Manager',))
- row = cursor.fetchone()
- vertex = row[0]
- title1 = vertex["title"]
- print("SET title: ", title1)
-
- ag.commit()
-
- cursor = ag.execCypher("MATCH (p:Person {name: 'Smith'}) RETURN
p.title")
- row = cursor.fetchone()
- title2 = row[0]
-
- self.assertEqual(title1, title2)
-
- cursor = ag.execCypher("MATCH (n:Person {name: %s}) SET
n.bigNum=-6.45161e+46::numeric RETURN n", params=('Smith',))
- row = cursor.fetchone()
- vertex = row[0]
- for row in cursor:
- print("SET bigNum: ", vertex['bigNum'])
-
- bigNum1 = vertex["bigNum"]
-
- self.assertEqual(decimal.Decimal("-6.45161e+46"), bigNum1)
- ag.commit()
-
-
- cursor = ag.execCypher("MATCH (p:Person {name: 'Smith'}) RETURN
p.bigNum")
- row = cursor.fetchone()
- bigNum2 = row[0]
-
- self.assertEqual(bigNum1, bigNum2)
-
-
- cursor = ag.execCypher("MATCH (n:Person {name: %s}) REMOVE n.title
RETURN n", params=('Smith',))
- for row in cursor:
- print("REMOVE Prop title: ", row[0])
- #Assert that the title property is removed
- self.assertIsNone(row[0].properties.get('title'))
- print("\nTest 3 Successful....")
-
- # You must commit explicitly
- ag.commit()
-
-
- def testCypher(self):
-
- print("\n--------------------------")
- print("Test 4: Testing Cypher.....")
- print("--------------------------\n")
-
- ag = self.ag
-
- with ag.connection.cursor() as cursor:
- try :
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Joe',))
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Jack',))
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Andy',))
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Smith',))
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Tom',))
-
- # You must commit explicitly
- ag.commit()
- except Exception as ex:
- print(ex)
- ag.rollback()
-
- with ag.connection.cursor() as cursor:
- try :# Create Edges
- ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name =
'Joe' AND b.name = 'Smith' CREATE (a)-[r:worksWith {weight: 3}]->(b)")
- ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name =
'Andy' AND b.name = 'Tom' CREATE (a)-[r:worksWith {weight: 1}]->(b)")
- ag.cypher(cursor,"MATCH (a:Person {name: 'Jack'}), (b:Person
{name: 'Andy'}) CREATE (a)-[r:worksWith {weight: 5}]->(b)")
-
- # You must commit explicitly
- ag.commit()
- except Exception as ex:
- print(ex)
- ag.rollback()
-
-
- # With Params
- cursor = ag.execCypher("""MATCH (a:Person), (b:Person)
- WHERE a.name = %s AND b.name = %s
- CREATE p=((a)-[r:worksWith]->(b)) RETURN p""",
- params=('Andy', 'Smith',))
-
- for row in cursor:
- print("CREATED EDGE: %s" % row[0])
-
- cursor = ag.execCypher("""MATCH (a:Person {name: 'Joe'}), (b:Person
{name: 'Jack'})
- CREATE p=((a)-[r:worksWith {weight: 5}]->(b))
- RETURN p """)
-
- for row in cursor:
- print("CREATED EDGE WITH PROPERTIES: %s" % row[0])
- self.assertEqual(row[0][1].properties['weight'], 5)
-
- print("\nTest 4 Successful...")
-
-
-
- def testMultipleEdges(self):
-
- print("\n------------------------------------")
- print("Test 5: Testing Multiple Edges.....")
- print("------------------------------------\n")
-
- ag = self.ag
- with ag.connection.cursor() as cursor:
- try :
- ag.cypher(cursor, "CREATE (n:Country {name: %s}) ",
params=('USA',))
- ag.cypher(cursor, "CREATE (n:Country {name: %s}) ",
params=('France',))
- ag.cypher(cursor, "CREATE (n:Country {name: %s}) ",
params=('Korea',))
- ag.cypher(cursor, "CREATE (n:Country {name: %s}) ",
params=('Russia',))
-
- # You must commit explicitly after all executions.
- ag.connection.commit()
- except Exception as ex:
- ag.rollback()
- raise ex
-
- with ag.connection.cursor() as cursor:
- try :# Create Edges
- ag.cypher(cursor,"MATCH (a:Country), (b:Country) WHERE a.name
= 'USA' AND b.name = 'France' CREATE (a)-[r:distance {unit:'miles', value:
4760}]->(b)")
- ag.cypher(cursor,"MATCH (a:Country), (b:Country) WHERE a.name
= 'France' AND b.name = 'Korea' CREATE (a)-[r:distance {unit: 'km', value:
9228}]->(b)")
- ag.cypher(cursor,"MATCH (a:Country {name: 'Korea'}),
(b:Country {name: 'Russia'}) CREATE (a)-[r:distance {unit:'km', value:
3078}]->(b)")
-
- # You must commit explicitly
- ag.connection.commit()
- except Exception as ex:
- ag.rollback()
- raise ex
-
-
- cursor = ag.execCypher("""MATCH p=(:Country
{name:"USA"})-[:distance]-(:Country)-[:distance]-(:Country)
- RETURN p""")
-
- count = 0
- output = []
- for row in cursor:
- path = row[0]
- for e in path:
- if e.gtype == age.TP_VERTEX:
- output.append(e.label + " " + e["name"])
- elif e.gtype == age.TP_EDGE:
- output.append("---- (distance " + str(e["value"]) + " " +
e["unit"] + ") --->")
- else:
- output.append("Unknown element. " + str(e))
-
- count += 1
-
- formatted_output = " ".join(output)
- print("PATH WITH MULTIPLE EDGES: %s" % formatted_output)
- self.assertEqual(5,count)
-
- print("\nTest 5 Successful...")
-
-
-
- def testCollect(self):
-
- print("\n--------------------------")
- print("Test 6: Testing COLLECT.....")
- print("--------------------------\n")
-
- ag = self.ag
-
- with ag.connection.cursor() as cursor:
- try :
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Joe',))
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Jack',))
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Andy',))
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Smith',))
- ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Tom',))
-
- # You must commit explicitly
- ag.commit()
- except Exception as ex:
- print(ex)
- ag.rollback()
-
- with ag.connection.cursor() as cursor:
- try :# Create Edges
- ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name =
'Joe' AND b.name = 'Smith' CREATE (a)-[r:worksWith {weight: 3}]->(b)")
- ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name =
'Joe' AND b.name = 'Tom' CREATE (a)-[r:worksWith {weight: 1}]->(b)")
- ag.cypher(cursor,"MATCH (a:Person {name: 'Joe'}), (b:Person
{name: 'Andy'}) CREATE (a)-[r:worksWith {weight: 5}]->(b)")
-
- # You must commit explicitly
- ag.commit()
- except Exception as ex:
- print(ex)
- ag.rollback()
-
- print(" -------- TESTING COLLECT #1 --------")
- with ag.connection.cursor() as cursor:
- ag.cypher(cursor, "MATCH (a)-[:worksWith]->(c) WITH a as V,
COLLECT(c) as CV RETURN V.name, CV", cols=["V","CV"])
- for row in cursor:
- nm = row[0]
- collected = row[1]
- print(nm, "worksWith", [i["name"] for i in collected])
- self.assertEqual(3,len(collected))
-
-
- print(" -------- TESTING COLLECT #2 --------")
- for row in ag.execCypher("MATCH (a)-[:worksWith]->(c) WITH a as V,
COLLECT(c) as CV RETURN V.name, CV", cols=["V1","CV"]):
- nm = row[0]
- collected = row[1]
- print(nm, "worksWith", [i["name"] for i in collected])
- self.assertEqual(3,len(collected))
- print("\nTest 6 Successful...")
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser()
-
- parser.add_argument('-host',
- '--host',
- help='Optional Host Name. Default Host is "127.0.0.1"
',
- default="127.0.0.1")
- parser.add_argument('-port',
- '--port',
- help='Optional Port Number. Default port no is 5432',
- default=5432)
- parser.add_argument('-db',
- '--database',
- help='Required Database Name',
- required=True)
- parser.add_argument('-u',
- '--user',
- help='Required Username Name',
- required=True)
- parser.add_argument('-pass',
- '--password',
- help='Required Password for authentication',
- required=True)
- parser.add_argument('-gn',
- '--graphName',
- help='Optional Graph Name to be created. Default
graphName is "test_graph"',
- default="test_graph")
-
- args = parser.parse_args()
- suite = unittest.TestSuite()
- suite.addTest(TestAgeBasic('testExec'))
- suite.addTest(TestAgeBasic('testQuery'))
- suite.addTest(TestAgeBasic('testChangeData'))
- suite.addTest(TestAgeBasic('testCypher'))
- suite.addTest(TestAgeBasic('testMultipleEdges'))
- suite.addTest(TestAgeBasic('testCollect'))
- TestAgeBasic.args = args
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import json
+
+from age.models import Vertex
+import unittest
+import decimal
+import age
+import argparse
+
+DSN = "host=localhost port=5432 dbname=postgres user=dehowefeng password=agens"
+TEST_HOST = "localhost"
+TEST_PORT = 5432
+TEST_DB = "postgres"
+TEST_USER = "postgres"
+TEST_PASSWORD = "agens"
+TEST_GRAPH_NAME = "test_graph"
+
+class TestAgeBasic(unittest.TestCase):
+ ag = None
+ def setUp(self):
+ print("Connecting to Test Graph.....")
+ self.ag = age.connect(graph=TEST_GRAPH_NAME, host=TEST_HOST,
port=TEST_PORT, dbname=TEST_DB, user=TEST_USER, password=TEST_PASSWORD)
+
+
+ def tearDown(self):
+ # Clear test data
+ print("Deleting Test Graph.....")
+ age.deleteGraph(self.ag.connection, self.ag.graphName)
+ self.ag.close()
+
+ def testExec(self):
+
+ print("\n---------------------------------------------------")
+ print("Test 1: Checking single and multi column Returns.....")
+ print("---------------------------------------------------\n")
+
+ ag = self.ag
+ # Create and Return single column
+ cursor = ag.execCypher("CREATE (n:Person {name: %s, title:
'Developer'}) RETURN n", params=('Andy',))
+ for row in cursor:
+ print("Vertex: %s , Type: %s " % (Vertex, type(row[0])))
+
+
+ # Create and Return multi columns
+ cursor = ag.execCypher("CREATE (n:Person {name: %s, title: %s}) RETURN
id(n), n.name", cols=['id','name'], params=('Jack','Manager'))
+ row = cursor.fetchone()
+ print("Id: %s , Name: %s" % (row[0], row[1]))
+ self.assertEqual(int, type(row[0]))
+ ag.commit()
+ print("\nTest 1 Successful....")
+
+
+
+
+ def testQuery(self):
+
+ print("\n--------------------------------------------------")
+ print("Test 2: Testing CREATE and query relationships....." )
+ print("--------------------------------------------------\n")
+
+ ag = self.ag
+ ag.execCypher("CREATE (n:Person {name: %s}) ", params=('Jack',))
+ ag.execCypher("CREATE (n:Person {name: %s}) ", params=('Andy',))
+ ag.execCypher("CREATE (n:Person {name: %s}) ", params=('Smith',))
+ ag.execCypher("MATCH (a:Person), (b:Person) WHERE a.name = 'Andy' AND
b.name = 'Jack' CREATE (a)-[r:worksWith {weight: 3}]->(b)")
+ ag.execCypher("""MATCH (a:Person), (b:Person)
+ WHERE a.name = %s AND b.name = %s
+ CREATE p=((a)-[r:worksWith]->(b)) """, params=('Jack',
'Smith',))
+
+ ag.commit()
+
+ cursor = ag.execCypher("MATCH p=()-[:worksWith]-() RETURN p")
+ for row in cursor:
+ path = row[0]
+ print("START:", path[0])
+ print("EDGE:", path[1])
+ print("END:", path[2])
+
+ cursor = ag.execCypher("MATCH p=(a)-[b]-(c) WHERE b.weight>2 RETURN
a,label(b), b.weight, c", cols=["a","bl","bw", "c"], params=(2,))
+ for row in cursor:
+ start = row[0]
+ edgel = row[1]
+ edgew = row[2]
+ end = row[3]
+ print("Relationship: %s %s %s. Edge weight: %s" % (start["name"] ,
edgel,end["name"], edgew))
+ #Assert that the weight of the edge is greater than 2
+ self.assertEqual(edgew > 2, True)
+ print("\nTest 2 Successful...")
+
+
+ def testChangeData(self):
+
+ print("\n-------------------------------------------------------")
+ print("Test 3: Testing changes in data using SET and REMOVE.....")
+ print("-------------------------------------------------------\n")
+
+ ag = self.ag
+ # Create Vertices
+ # Commit automatically
+ ag.execCypher("CREATE (n:Person {name: 'Joe'})")
+
+ cursor = ag.execCypher("CREATE (n:Person {name: %s, title:
'Developer'}) RETURN n", params=('Smith',))
+ row = cursor.fetchone()
+ print("CREATED: ", row[0])
+
+ # You must commit explicitly
+ ag.commit()
+
+ cursor = ag.execCypher("MATCH (n:Person {name: %s}) SET n.title=%s
RETURN n", params=('Smith','Manager',))
+ row = cursor.fetchone()
+ vertex = row[0]
+ title1 = vertex["title"]
+ print("SET title: ", title1)
+
+ ag.commit()
+
+ cursor = ag.execCypher("MATCH (p:Person {name: 'Smith'}) RETURN
p.title")
+ row = cursor.fetchone()
+ title2 = row[0]
+
+ self.assertEqual(title1, title2)
+
+ cursor = ag.execCypher("MATCH (n:Person {name: %s}) SET
n.bigNum=-6.45161e+46::numeric RETURN n", params=('Smith',))
+ row = cursor.fetchone()
+ vertex = row[0]
+ for row in cursor:
+ print("SET bigNum: ", vertex['bigNum'])
+
+ bigNum1 = vertex["bigNum"]
+
+ self.assertEqual(decimal.Decimal("-6.45161e+46"), bigNum1)
+ ag.commit()
+
+
+ cursor = ag.execCypher("MATCH (p:Person {name: 'Smith'}) RETURN
p.bigNum")
+ row = cursor.fetchone()
+ bigNum2 = row[0]
+
+ self.assertEqual(bigNum1, bigNum2)
+
+
+ cursor = ag.execCypher("MATCH (n:Person {name: %s}) REMOVE n.title
RETURN n", params=('Smith',))
+ for row in cursor:
+ print("REMOVE Prop title: ", row[0])
+ #Assert that the title property is removed
+ self.assertIsNone(row[0].properties.get('title'))
+ print("\nTest 3 Successful....")
+
+ # You must commit explicitly
+ ag.commit()
+
+
+ def testCypher(self):
+
+ print("\n--------------------------")
+ print("Test 4: Testing Cypher.....")
+ print("--------------------------\n")
+
+ ag = self.ag
+
+ with ag.connection.cursor() as cursor:
+ try :
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Joe',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Jack',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Andy',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Smith',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Tom',))
+
+ # You must commit explicitly
+ ag.commit()
+ except Exception as ex:
+ print(ex)
+ ag.rollback()
+
+ with ag.connection.cursor() as cursor:
+ try :# Create Edges
+ ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name =
'Joe' AND b.name = 'Smith' CREATE (a)-[r:worksWith {weight: 3}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name =
'Andy' AND b.name = 'Tom' CREATE (a)-[r:worksWith {weight: 1}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person {name: 'Jack'}), (b:Person
{name: 'Andy'}) CREATE (a)-[r:worksWith {weight: 5}]->(b)")
+
+ # You must commit explicitly
+ ag.commit()
+ except Exception as ex:
+ print(ex)
+ ag.rollback()
+
+
+ # With Params
+ cursor = ag.execCypher("""MATCH (a:Person), (b:Person)
+ WHERE a.name = %s AND b.name = %s
+ CREATE p=((a)-[r:worksWith]->(b)) RETURN p""",
+ params=('Andy', 'Smith',))
+
+ for row in cursor:
+ print("CREATED EDGE: %s" % row[0])
+
+ cursor = ag.execCypher("""MATCH (a:Person {name: 'Joe'}), (b:Person
{name: 'Jack'})
+ CREATE p=((a)-[r:worksWith {weight: 5}]->(b))
+ RETURN p """)
+
+ for row in cursor:
+ print("CREATED EDGE WITH PROPERTIES: %s" % row[0])
+ self.assertEqual(row[0][1].properties['weight'], 5)
+
+ print("\nTest 4 Successful...")
+
+
+
+ def testMultipleEdges(self):
+
+ print("\n------------------------------------")
+ print("Test 5: Testing Multiple Edges.....")
+ print("------------------------------------\n")
+
+ ag = self.ag
+ with ag.connection.cursor() as cursor:
+ try :
+ ag.cypher(cursor, "CREATE (n:Country {name: %s}) ",
params=('USA',))
+ ag.cypher(cursor, "CREATE (n:Country {name: %s}) ",
params=('France',))
+ ag.cypher(cursor, "CREATE (n:Country {name: %s}) ",
params=('Korea',))
+ ag.cypher(cursor, "CREATE (n:Country {name: %s}) ",
params=('Russia',))
+
+ # You must commit explicitly after all executions.
+ ag.connection.commit()
+ except Exception as ex:
+ ag.rollback()
+ raise ex
+
+ with ag.connection.cursor() as cursor:
+ try :# Create Edges
+ ag.cypher(cursor,"MATCH (a:Country), (b:Country) WHERE a.name
= 'USA' AND b.name = 'France' CREATE (a)-[r:distance {unit:'miles', value:
4760}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Country), (b:Country) WHERE a.name
= 'France' AND b.name = 'Korea' CREATE (a)-[r:distance {unit: 'km', value:
9228}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Country {name: 'Korea'}),
(b:Country {name: 'Russia'}) CREATE (a)-[r:distance {unit:'km', value:
3078}]->(b)")
+
+ # You must commit explicitly
+ ag.connection.commit()
+ except Exception as ex:
+ ag.rollback()
+ raise ex
+
+
+ cursor = ag.execCypher("""MATCH p=(:Country
{name:"USA"})-[:distance]-(:Country)-[:distance]-(:Country)
+ RETURN p""")
+
+ count = 0
+ output = []
+ for row in cursor:
+ path = row[0]
+ for e in path:
+ if e.gtype == age.TP_VERTEX:
+ output.append(e.label + " " + e["name"])
+ elif e.gtype == age.TP_EDGE:
+ output.append("---- (distance " + str(e["value"]) + " " +
e["unit"] + ") --->")
+ else:
+ output.append("Unknown element. " + str(e))
+
+ count += 1
+
+ formatted_output = " ".join(output)
+ print("PATH WITH MULTIPLE EDGES: %s" % formatted_output)
+ self.assertEqual(5,count)
+
+ print("\nTest 5 Successful...")
+
+
+
+ def testCollect(self):
+
+ print("\n--------------------------")
+ print("Test 6: Testing COLLECT.....")
+ print("--------------------------\n")
+
+ ag = self.ag
+
+ with ag.connection.cursor() as cursor:
+ try :
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Joe',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Jack',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Andy',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Smith',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Tom',))
+
+ # You must commit explicitly
+ ag.commit()
+ except Exception as ex:
+ print(ex)
+ ag.rollback()
+
+ with ag.connection.cursor() as cursor:
+ try :# Create Edges
+ ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name =
'Joe' AND b.name = 'Smith' CREATE (a)-[r:worksWith {weight: 3}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person), (b:Person) WHERE a.name =
'Joe' AND b.name = 'Tom' CREATE (a)-[r:worksWith {weight: 1}]->(b)")
+ ag.cypher(cursor,"MATCH (a:Person {name: 'Joe'}), (b:Person
{name: 'Andy'}) CREATE (a)-[r:worksWith {weight: 5}]->(b)")
+
+ # You must commit explicitly
+ ag.commit()
+ except Exception as ex:
+ print(ex)
+ ag.rollback()
+
+ print(" -------- TESTING COLLECT #1 --------")
+ with ag.connection.cursor() as cursor:
+ ag.cypher(cursor, "MATCH (a)-[:worksWith]->(c) WITH a as V,
COLLECT(c) as CV RETURN V.name, CV", cols=["V","CV"])
+ for row in cursor:
+ nm = row[0]
+ collected = row[1]
+ print(nm, "worksWith", [i["name"] for i in collected])
+ self.assertEqual(3,len(collected))
+
+
+ print(" -------- TESTING COLLECT #2 --------")
+ for row in ag.execCypher("MATCH (a)-[:worksWith]->(c) WITH a as V,
COLLECT(c) as CV RETURN V.name, CV", cols=["V1","CV"]):
+ nm = row[0]
+ collected = row[1]
+ print(nm, "worksWith", [i["name"] for i in collected])
+ self.assertEqual(3,len(collected))
+ print("\nTest 6 Successful...")
+
+ def testSerialization(self):
+
+ print("\n---------------------------------------")
+ print("Test 6: Testing Vertex Serialization.....")
+ print("-----------------------------------------\n")
+
+ ag = self.ag
+
+ with ag.connection.cursor() as cursor:
+ try:
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Joe',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Jack',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Andy',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Smith',))
+ ag.cypher(cursor, "CREATE (n:Person {name: %s}) ",
params=('Tom',))
+
+ # You must commit explicitly
+ ag.commit()
+ except Exception as ex:
+ print(ex)
+ ag.rollback()
+
+ print(" -------- TESTING Output #1 --------")
+ cursor = ag.execCypher("MATCH (n) RETURN n")
+
+ for row in cursor:
+ vertex = row[0]
+ try:
+ # json.loads will fail if the json str is not properly
formatted
+ as_dict = json.loads(vertex.toJson())
+ print("Vertex.toJson() returns a correct json string.")
+ assert True
+ except:
+ assert False
+
+ print(" -------- TESTING Output #2 --------")
+ cursor = ag.execCypher("MATCH (n) RETURN n")
+
+ for row in cursor:
+ vertex = row[0]
+ as_str = vertex.toString()
+ # Checking if the trailing comma appears in .toString() output
+ self.assertFalse(as_str.endswith(", }}::VERTEX"))
+ print("Vertex.toString() 'properties' field is formatted properly.")
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument('-host',
+ '--host',
+ help='Optional Host Name. Default Host is "127.0.0.1"
',
+ default="127.0.0.1")
+ parser.add_argument('-port',
+ '--port',
+ help='Optional Port Number. Default port no is 5432',
+ default=5432)
+ parser.add_argument('-db',
+ '--database',
+ help='Required Database Name',
+ required=True)
+ parser.add_argument('-u',
+ '--user',
+ help='Required Username Name',
+ required=True)
+ parser.add_argument('-pass',
+ '--password',
+ help='Required Password for authentication',
+ required=True)
+ parser.add_argument('-gn',
+ '--graphName',
+ help='Optional Graph Name to be created. Default
graphName is "test_graph"',
+ default="test_graph")
+
+ args = parser.parse_args()
+ suite = unittest.TestSuite()
+ suite.addTest(TestAgeBasic('testExec'))
+ suite.addTest(TestAgeBasic('testQuery'))
+ suite.addTest(TestAgeBasic('testChangeData'))
+ suite.addTest(TestAgeBasic('testCypher'))
+ suite.addTest(TestAgeBasic('testMultipleEdges'))
+ suite.addTest(TestAgeBasic('testCollect'))
+ suite.addTest(TestAgeBasic('testSerialization'))
+ TestAgeBasic.args = args
unittest.TextTestRunner().run(suite)