diff --git a/drivers/python/age/models.py b/drivers/python/age/models.py index 838110a1c..aee1b7599 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 99c8d0857..1161fda8b 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)