| # 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 sys |
| import logging |
| import inspect |
| from packaging.version import Version |
| import ipaddress |
| from uuid import UUID |
| from decimal import Decimal |
| import datetime |
| |
| from cassandra.util import Point, LineString, Polygon, Duration |
| |
| from cassandra.cluster import EXEC_PROFILE_GRAPH_DEFAULT, EXEC_PROFILE_GRAPH_ANALYTICS_DEFAULT |
| from cassandra.cluster import GraphAnalyticsExecutionProfile, GraphExecutionProfile, EXEC_PROFILE_GRAPH_SYSTEM_DEFAULT, \ |
| default_lbp_factory |
| from cassandra.policies import DSELoadBalancingPolicy |
| |
| from cassandra.graph import GraphSON1Deserializer |
| from cassandra.graph.graphson import InetTypeIO, GraphSON2Deserializer, GraphSON3Deserializer |
| from cassandra.graph import Edge, Vertex, Path |
| from cassandra.graph.query import GraphOptions, GraphProtocol, graph_graphson2_row_factory, \ |
| graph_graphson3_row_factory |
| |
| from tests.integration import DSE_VERSION |
| from tests.integration.advanced import * |
| |
| |
| def setup_module(): |
| if DSE_VERSION: |
| dse_options = {'graph': {'realtime_evaluation_timeout_in_seconds': 60}} |
| use_single_node_with_graph(dse_options=dse_options) |
| |
| |
| log = logging.getLogger(__name__) |
| |
| MAX_LONG = 9223372036854775807 |
| MIN_LONG = -9223372036854775808 |
| ZERO_LONG = 0 |
| |
| MAKE_STRICT = "schema.config().option('graph.schema_mode').set('production')" |
| MAKE_NON_STRICT = "schema.config().option('graph.schema_mode').set('development')" |
| ALLOW_SCANS = "schema.config().option('graph.allow_scan').set('true')" |
| |
| deserializer_plus_to_ipaddressv4 = lambda x: ipaddress.IPv4Address(GraphSON1Deserializer.deserialize_inet(x)) |
| deserializer_plus_to_ipaddressv6 = lambda x: ipaddress.IPv6Address(GraphSON1Deserializer.deserialize_inet(x)) |
| |
| |
| def generic_ip_deserializer(string_ip_address): |
| if ":" in string_ip_address: |
| return deserializer_plus_to_ipaddressv6(string_ip_address) |
| return deserializer_plus_to_ipaddressv4(string_ip_address) |
| |
| |
| class GenericIpAddressIO(InetTypeIO): |
| @classmethod |
| def deserialize(cls, value, reader=None): |
| return generic_ip_deserializer(value) |
| |
| GraphSON2Deserializer._deserializers[GenericIpAddressIO.graphson_type] = GenericIpAddressIO |
| GraphSON3Deserializer._deserializers[GenericIpAddressIO.graphson_type] = GenericIpAddressIO |
| |
| if DSE_VERSION: |
| if DSE_VERSION >= Version('6.8.0'): |
| CREATE_CLASSIC_GRAPH = "system.graph(name).engine(Classic).create()" |
| else: |
| CREATE_CLASSIC_GRAPH = "system.graph(name).create()" |
| |
| |
| def reset_graph(session, graph_name): |
| ks = list(session.execute( |
| "SELECT * FROM system_schema.keyspaces WHERE keyspace_name = '{}';".format(graph_name))) |
| if ks: |
| try: |
| session.execute_graph('system.graph(name).drop()', {'name': graph_name}, |
| execution_profile=EXEC_PROFILE_GRAPH_SYSTEM_DEFAULT) |
| except: |
| pass |
| |
| session.execute_graph(CREATE_CLASSIC_GRAPH, {'name': graph_name}, |
| execution_profile=EXEC_PROFILE_GRAPH_SYSTEM_DEFAULT) |
| wait_for_graph_inserted(session, graph_name) |
| |
| |
| def wait_for_graph_inserted(session, graph_name): |
| count = 0 |
| exists = session.execute_graph('system.graph(name).exists()', {'name': graph_name}, |
| execution_profile=EXEC_PROFILE_GRAPH_SYSTEM_DEFAULT)[0].value |
| while not exists and count < 50: |
| time.sleep(1) |
| exists = session.execute_graph('system.graph(name).exists()', {'name': graph_name}, |
| execution_profile=EXEC_PROFILE_GRAPH_SYSTEM_DEFAULT)[0].value |
| return exists |
| |
| |
| class BasicGraphUnitTestCase(BasicKeyspaceUnitTestCase): |
| """ |
| This is basic graph unit test case that provides various utility methods that can be leveraged for testcase setup and tear |
| down |
| """ |
| |
| @property |
| def graph_name(self): |
| return self._testMethodName.lower() |
| |
| def session_setup(self): |
| lbp = DSELoadBalancingPolicy(default_lbp_factory()) |
| |
| ep_graphson2 = GraphExecutionProfile( |
| request_timeout=60, |
| load_balancing_policy=lbp, |
| graph_options=GraphOptions( |
| graph_name=self.graph_name, |
| graph_protocol=GraphProtocol.GRAPHSON_2_0 |
| ), |
| row_factory=graph_graphson2_row_factory) |
| |
| ep_graphson3 = GraphExecutionProfile( |
| request_timeout=60, |
| load_balancing_policy=lbp, |
| graph_options=GraphOptions( |
| graph_name=self.graph_name, |
| graph_protocol=GraphProtocol.GRAPHSON_3_0 |
| ), |
| row_factory=graph_graphson3_row_factory) |
| |
| ep_graphson1 = GraphExecutionProfile( |
| request_timeout=60, |
| load_balancing_policy=lbp, |
| graph_options=GraphOptions( |
| graph_name=self.graph_name |
| ) |
| ) |
| |
| ep_analytics = GraphAnalyticsExecutionProfile( |
| request_timeout=60, |
| load_balancing_policy=lbp, |
| graph_options=GraphOptions( |
| graph_source=b'a', |
| graph_language=b'gremlin-groovy', |
| graph_name=self.graph_name |
| ) |
| ) |
| |
| self.cluster = TestCluster(execution_profiles={ |
| EXEC_PROFILE_GRAPH_DEFAULT: ep_graphson1, |
| EXEC_PROFILE_GRAPH_ANALYTICS_DEFAULT: ep_analytics, |
| "graphson1": ep_graphson1, |
| "graphson2": ep_graphson2, |
| "graphson3": ep_graphson3 |
| }) |
| |
| self.session = self.cluster.connect() |
| self.ks_name = self._testMethodName.lower() |
| self.cass_version, self.cql_version = get_server_versions() |
| |
| def setUp(self): |
| self.session_setup() |
| self.reset_graph() |
| self.clear_schema() |
| # enable dev and scan modes |
| self.session.execute_graph(MAKE_NON_STRICT) |
| self.session.execute_graph(ALLOW_SCANS) |
| |
| def tearDown(self): |
| self.cluster.shutdown() |
| |
| def clear_schema(self): |
| self.session.execute_graph(""" |
| schema.clear(); |
| """) |
| |
| def reset_graph(self): |
| reset_graph(self.session, self.graph_name) |
| |
| def wait_for_graph_inserted(self): |
| wait_for_graph_inserted(self.session, self.graph_name) |
| |
| def _execute(self, query, graphson, params=None, execution_profile_options=None, **kwargs): |
| queries = query if isinstance(query, list) else [query] |
| ep = self.get_execution_profile(graphson) |
| if execution_profile_options: |
| ep = self.session.execution_profile_clone_update(ep, **execution_profile_options) |
| |
| results = [] |
| for query in queries: |
| log.debug(query) |
| rf = self.session.execute_graph_async(query, parameters=params, execution_profile=ep, **kwargs) |
| results.append(rf.result()) |
| self.assertEqual(rf.message.custom_payload['graph-results'], graphson) |
| |
| return results[0] if len(results) == 1 else results |
| |
| def get_execution_profile(self, graphson, traversal=False): |
| ep = 'graphson1' |
| if graphson == GraphProtocol.GRAPHSON_2_0: |
| ep = 'graphson2' |
| elif graphson == GraphProtocol.GRAPHSON_3_0: |
| ep = 'graphson3' |
| |
| return ep if traversal is False else 'traversal_' + ep |
| |
| def resultset_to_list(self, rs): |
| results_list = [] |
| for result in rs: |
| try: |
| results_list.append(result.value) |
| except: |
| results_list.append(result) |
| |
| return results_list |
| |
| |
| class GraphUnitTestCase(BasicKeyspaceUnitTestCase): |
| |
| @property |
| def graph_name(self): |
| return self._testMethodName.lower() |
| |
| def session_setup(self): |
| lbp = DSELoadBalancingPolicy(default_lbp_factory()) |
| |
| ep_graphson2 = GraphExecutionProfile( |
| request_timeout=60, |
| load_balancing_policy=lbp, |
| graph_options=GraphOptions( |
| graph_name=self.graph_name, |
| graph_protocol=GraphProtocol.GRAPHSON_2_0 |
| ), |
| row_factory=graph_graphson2_row_factory) |
| |
| ep_graphson3 = GraphExecutionProfile( |
| request_timeout=60, |
| load_balancing_policy=lbp, |
| graph_options=GraphOptions( |
| graph_name=self.graph_name, |
| graph_protocol=GraphProtocol.GRAPHSON_3_0 |
| ), |
| row_factory=graph_graphson3_row_factory) |
| |
| ep_graphson1 = GraphExecutionProfile( |
| request_timeout=60, |
| load_balancing_policy=lbp, |
| graph_options=GraphOptions( |
| graph_name=self.graph_name, |
| graph_language='gremlin-groovy' |
| ) |
| ) |
| |
| ep_analytics = GraphAnalyticsExecutionProfile( |
| request_timeout=60, |
| load_balancing_policy=lbp, |
| graph_options=GraphOptions( |
| graph_source=b'a', |
| graph_language=b'gremlin-groovy', |
| graph_name=self.graph_name |
| ) |
| ) |
| |
| self.cluster = TestCluster(execution_profiles={ |
| EXEC_PROFILE_GRAPH_DEFAULT: ep_graphson1, |
| EXEC_PROFILE_GRAPH_ANALYTICS_DEFAULT: ep_analytics, |
| "graphson1": ep_graphson1, |
| "graphson2": ep_graphson2, |
| "graphson3": ep_graphson3 |
| }) |
| |
| self.session = self.cluster.connect() |
| self.ks_name = self._testMethodName.lower() |
| self.cass_version, self.cql_version = get_server_versions() |
| |
| def setUp(self): |
| """basic setup only""" |
| self.session_setup() |
| |
| def setup_graph(self, schema): |
| """Config dependant setup""" |
| schema.drop_graph(self.session, self.graph_name) |
| schema.create_graph(self.session, self.graph_name) |
| schema.clear(self.session) |
| if schema is ClassicGraphSchema: |
| # enable dev and scan modes |
| self.session.execute_graph(MAKE_NON_STRICT) |
| self.session.execute_graph(ALLOW_SCANS) |
| |
| def teardown_graph(self, schema): |
| schema.drop_graph(self.session, self.graph_name) |
| |
| def tearDown(self): |
| self.cluster.shutdown() |
| |
| def execute_graph_queries(self, queries, params=None, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT, |
| verify_graphson=False, **kwargs): |
| results = [] |
| for query in queries: |
| log.debug(query) |
| rf = self.session.execute_graph_async(query, parameters=params, |
| execution_profile=execution_profile, **kwargs) |
| if verify_graphson: |
| self.assertEqual(rf.message.custom_payload['graph-results'], verify_graphson) |
| results.append(rf.result()) |
| |
| return results |
| |
| def execute_graph(self, query, graphson, params=None, execution_profile_options=None, traversal=False, **kwargs): |
| queries = query if isinstance(query, list) else [query] |
| ep = self.get_execution_profile(graphson) |
| if traversal: |
| ep = 'traversal_' + ep |
| if execution_profile_options: |
| ep = self.session.execution_profile_clone_update(ep, **execution_profile_options) |
| |
| results = self.execute_graph_queries(queries, params, ep, verify_graphson=graphson, **kwargs) |
| |
| return results[0] if len(results) == 1 else results |
| |
| def get_execution_profile(self, graphson, traversal=False): |
| ep = 'graphson1' |
| if graphson == GraphProtocol.GRAPHSON_2_0: |
| ep = 'graphson2' |
| elif graphson == GraphProtocol.GRAPHSON_3_0: |
| ep = 'graphson3' |
| |
| return ep if traversal is False else 'traversal_' + ep |
| |
| def resultset_to_list(self, rs): |
| results_list = [] |
| for result in rs: |
| try: |
| results_list.append(result.value) |
| except: |
| results_list.append(result) |
| |
| return results_list |
| |
| |
| class BasicSharedGraphUnitTestCase(BasicKeyspaceUnitTestCase): |
| """ |
| This is basic graph unit test case that provides various utility methods that can be leveraged for testcase setup and tear |
| down |
| """ |
| |
| @classmethod |
| def session_setup(cls): |
| cls.cluster = TestCluster() |
| cls.session = cls.cluster.connect() |
| cls.ks_name = cls.__name__.lower() |
| cls.cass_version, cls.cql_version = get_server_versions() |
| cls.graph_name = cls.__name__.lower() |
| |
| @classmethod |
| def setUpClass(cls): |
| if DSE_VERSION: |
| cls.session_setup() |
| cls.reset_graph() |
| profiles = cls.cluster.profile_manager.profiles |
| profiles[EXEC_PROFILE_GRAPH_DEFAULT].request_timeout = 60 |
| profiles[EXEC_PROFILE_GRAPH_DEFAULT].graph_options.graph_name = cls.graph_name |
| profiles[EXEC_PROFILE_GRAPH_ANALYTICS_DEFAULT].request_timeout = 60 |
| profiles[EXEC_PROFILE_GRAPH_ANALYTICS_DEFAULT].graph_options.graph_name = cls.graph_name |
| |
| @classmethod |
| def tearDownClass(cls): |
| if DSE_VERSION: |
| cls.cluster.shutdown() |
| |
| @classmethod |
| def clear_schema(self): |
| self.session.execute_graph('schema.clear()') |
| |
| @classmethod |
| def reset_graph(self): |
| reset_graph(self.session, self.graph_name) |
| |
| def wait_for_graph_inserted(self): |
| wait_for_graph_inserted(self.session, self.graph_name) |
| |
| |
| class GraphFixtures(object): |
| |
| @staticmethod |
| def line(length, single_script=True): |
| raise NotImplementedError() |
| |
| @staticmethod |
| def classic(): |
| raise NotImplementedError() |
| |
| @staticmethod |
| def multiple_fields(): |
| raise NotImplementedError() |
| |
| @staticmethod |
| def large(): |
| raise NotImplementedError() |
| |
| |
| class ClassicGraphFixtures(GraphFixtures): |
| |
| @staticmethod |
| def datatypes(): |
| data = { |
| "boolean1": ["Boolean()", True, None], |
| "boolean2": ["Boolean()", False, None], |
| "point1": ["Point()", Point(.5, .13), GraphSON1Deserializer.deserialize_point], |
| "point2": ["Point()", Point(-5, .0), GraphSON1Deserializer.deserialize_point], |
| |
| "linestring1": ["Linestring()", LineString(((1.0, 2.0), (3.0, 4.0), (-89.0, 90.0))), |
| GraphSON1Deserializer.deserialize_linestring], |
| "polygon1": ["Polygon()", Polygon([(10.0, 10.0), (80.0, 10.0), (80., 88.0), (10., 89.0), (10., 10.0)], |
| [[(20., 20.0), (20., 30.0), (30., 30.0), (30., 20.0), (20., 20.0)], |
| [(40., 20.0), (40., 30.0), (50., 30.0), (50., 20.0), (40., 20.0)]]), |
| GraphSON1Deserializer.deserialize_polygon], |
| "int1": ["Int()", 2, GraphSON1Deserializer.deserialize_int], |
| "smallint1": ["Smallint()", 1, GraphSON1Deserializer.deserialize_smallint], |
| "bigint1": ["Bigint()", MAX_LONG, GraphSON1Deserializer.deserialize_bigint], |
| "bigint2": ["Bigint()", MIN_LONG, GraphSON1Deserializer.deserialize_bigint], |
| "bigint3": ["Bigint()", ZERO_LONG, GraphSON1Deserializer.deserialize_bigint], |
| "varint1": ["Varint()", 2147483647, GraphSON1Deserializer.deserialize_varint], |
| "int1": ["Int()", 100, GraphSON1Deserializer.deserialize_int], |
| "float1": ["Float()", 0.3415681, GraphSON1Deserializer.deserialize_float], |
| "double1": ["Double()", 0.34156811237335205, GraphSON1Deserializer.deserialize_double], |
| "uuid1": ["Uuid()", UUID('12345678123456781234567812345678'), GraphSON1Deserializer.deserialize_uuid], |
| "decimal1": ["Decimal()", Decimal(10), GraphSON1Deserializer.deserialize_decimal], |
| "blob1": ["Blob()", bytearray(b"Hello World"), GraphSON1Deserializer.deserialize_blob], |
| |
| "timestamp1": ["Timestamp()", datetime.datetime.utcnow().replace(microsecond=0), |
| GraphSON1Deserializer.deserialize_timestamp], |
| "timestamp2": ["Timestamp()", datetime.datetime.max.replace(microsecond=0), |
| GraphSON1Deserializer.deserialize_timestamp], |
| # These are valid values but are pending for DSP-14093 to be fixed |
| #"timestamp3": ["Timestamp()", datetime.datetime(159, 1, 1, 23, 59, 59), |
| # GraphSON1TypeDeserializer.deserialize_timestamp], |
| #"timestamp4": ["Timestamp()", datetime.datetime.min, |
| # GraphSON1TypeDeserializer.deserialize_timestamp], |
| "inet1": ["Inet()", ipaddress.IPv4Address(u"127.0.0.1"), deserializer_plus_to_ipaddressv4], |
| "inet2": ["Inet()", ipaddress.IPv6Address(u"2001:db8:85a3:8d3:1319:8a2e:370:7348"), |
| deserializer_plus_to_ipaddressv6], |
| "duration1": ["Duration()", datetime.timedelta(1, 16, 0), |
| GraphSON1Deserializer.deserialize_duration], |
| "duration2": ["Duration()", datetime.timedelta(days=1, seconds=16, milliseconds=15), |
| GraphSON1Deserializer.deserialize_duration], |
| "blob3": ["Blob()", bytes(b"Hello World Again"), GraphSON1Deserializer.deserialize_blob], |
| "blob4": ["Blob()", memoryview(b"And Again Hello World"), GraphSON1Deserializer.deserialize_blob] |
| } |
| |
| if DSE_VERSION >= Version("5.1"): |
| data["time1"] = ["Time()", datetime.time(12, 6, 12, 444), GraphSON1Deserializer.deserialize_time] |
| data["time2"] = ["Time()", datetime.time(12, 6, 12), GraphSON1Deserializer.deserialize_time] |
| data["time3"] = ["Time()", datetime.time(12, 6), GraphSON1Deserializer.deserialize_time] |
| data["time4"] = ["Time()", datetime.time.min, GraphSON1Deserializer.deserialize_time] |
| data["time5"] = ["Time()", datetime.time.max, GraphSON1Deserializer.deserialize_time] |
| data["blob5"] = ["Blob()", bytearray(b"AKDLIElksadlaswqA" * 10000), GraphSON1Deserializer.deserialize_blob] |
| data["datetime1"] = ["Date()", datetime.date.today(), GraphSON1Deserializer.deserialize_date] |
| data["datetime2"] = ["Date()", datetime.date(159, 1, 3), GraphSON1Deserializer.deserialize_date] |
| data["datetime3"] = ["Date()", datetime.date.min, GraphSON1Deserializer.deserialize_date] |
| data["datetime4"] = ["Date()", datetime.date.max, GraphSON1Deserializer.deserialize_date] |
| data["time1"] = ["Time()", datetime.time(12, 6, 12, 444), GraphSON1Deserializer.deserialize_time] |
| data["time2"] = ["Time()", datetime.time(12, 6, 12), GraphSON1Deserializer.deserialize_time] |
| data["time3"] = ["Time()", datetime.time(12, 6), GraphSON1Deserializer.deserialize_time] |
| data["time4"] = ["Time()", datetime.time.min, GraphSON1Deserializer.deserialize_time] |
| data["time5"] = ["Time()", datetime.time.max, GraphSON1Deserializer.deserialize_time] |
| |
| return data |
| |
| @staticmethod |
| def line(length, single_script=False): |
| queries = [ALLOW_SCANS + ';', |
| """schema.propertyKey('index').Int().ifNotExists().create(); |
| schema.propertyKey('distance').Int().ifNotExists().create(); |
| schema.vertexLabel('lp').properties('index').ifNotExists().create(); |
| schema.edgeLabel('goesTo').properties('distance').connection('lp', 'lp').ifNotExists().create();"""] |
| |
| vertex_script = ["Vertex vertex0 = graph.addVertex(label, 'lp', 'index', 0);"] |
| for index in range(1, length): |
| if not single_script and len(vertex_script) > 25: |
| queries.append("\n".join(vertex_script)) |
| vertex_script = [ |
| "Vertex vertex{pindex} = g.V().hasLabel('lp').has('index', {pindex}).next()".format( |
| pindex=index-1)] |
| |
| vertex_script.append(''' |
| Vertex vertex{vindex} = graph.addVertex(label, 'lp', 'index', {vindex}); |
| vertex{pindex}.addEdge('goesTo', vertex{vindex}, 'distance', 5); '''.format( |
| vindex=index, pindex=index - 1)) |
| |
| queries.append("\n".join(vertex_script)) |
| return queries |
| |
| @staticmethod |
| def classic(): |
| queries = [ALLOW_SCANS, |
| '''schema.propertyKey('name').Text().ifNotExists().create(); |
| schema.propertyKey('age').Int().ifNotExists().create(); |
| schema.propertyKey('lang').Text().ifNotExists().create(); |
| schema.propertyKey('weight').Float().ifNotExists().create(); |
| schema.vertexLabel('person').properties('name', 'age').ifNotExists().create(); |
| schema.vertexLabel('software').properties('name', 'lang').ifNotExists().create(); |
| schema.edgeLabel('created').properties('weight').connection('person', 'software').ifNotExists().create(); |
| schema.edgeLabel('created').connection('software', 'software').add(); |
| schema.edgeLabel('knows').properties('weight').connection('person', 'person').ifNotExists().create();''', |
| |
| '''Vertex marko = graph.addVertex(label, 'person', 'name', 'marko', 'age', 29); |
| Vertex vadas = graph.addVertex(label, 'person', 'name', 'vadas', 'age', 27); |
| Vertex lop = graph.addVertex(label, 'software', 'name', 'lop', 'lang', 'java'); |
| Vertex josh = graph.addVertex(label, 'person', 'name', 'josh', 'age', 32); |
| Vertex ripple = graph.addVertex(label, 'software', 'name', 'ripple', 'lang', 'java'); |
| Vertex peter = graph.addVertex(label, 'person', 'name', 'peter', 'age', 35); |
| Vertex carl = graph.addVertex(label, 'person', 'name', 'carl', 'age', 35); |
| marko.addEdge('knows', vadas, 'weight', 0.5f); |
| marko.addEdge('knows', josh, 'weight', 1.0f); |
| marko.addEdge('created', lop, 'weight', 0.4f); |
| josh.addEdge('created', ripple, 'weight', 1.0f); |
| josh.addEdge('created', lop, 'weight', 0.4f); |
| peter.addEdge('created', lop, 'weight', 0.2f);'''] |
| |
| return "\n".join(queries) |
| |
| @staticmethod |
| def multiple_fields(): |
| query_params = {} |
| queries= [ALLOW_SCANS, |
| '''schema.propertyKey('shortvalue').Smallint().ifNotExists().create(); |
| schema.vertexLabel('shortvertex').properties('shortvalue').ifNotExists().create(); |
| short s1 = 5000; graph.addVertex(label, "shortvertex", "shortvalue", s1); |
| schema.propertyKey('intvalue').Int().ifNotExists().create(); |
| schema.vertexLabel('intvertex').properties('intvalue').ifNotExists().create(); |
| int i1 = 1000000000; graph.addVertex(label, "intvertex", "intvalue", i1); |
| schema.propertyKey('intvalue2').Int().ifNotExists().create(); |
| schema.vertexLabel('intvertex2').properties('intvalue2').ifNotExists().create(); |
| Integer i2 = 100000000; graph.addVertex(label, "intvertex2", "intvalue2", i2); |
| schema.propertyKey('longvalue').Bigint().ifNotExists().create(); |
| schema.vertexLabel('longvertex').properties('longvalue').ifNotExists().create(); |
| long l1 = 9223372036854775807; graph.addVertex(label, "longvertex", "longvalue", l1); |
| schema.propertyKey('longvalue2').Bigint().ifNotExists().create(); |
| schema.vertexLabel('longvertex2').properties('longvalue2').ifNotExists().create(); |
| Long l2 = 100000000000000000L; graph.addVertex(label, "longvertex2", "longvalue2", l2); |
| schema.propertyKey('floatvalue').Float().ifNotExists().create(); |
| schema.vertexLabel('floatvertex').properties('floatvalue').ifNotExists().create(); |
| float f1 = 3.5f; graph.addVertex(label, "floatvertex", "floatvalue", f1); |
| schema.propertyKey('doublevalue').Double().ifNotExists().create(); |
| schema.vertexLabel('doublevertex').properties('doublevalue').ifNotExists().create(); |
| double d1 = 3.5e40; graph.addVertex(label, "doublevertex", "doublevalue", d1); |
| schema.propertyKey('doublevalue2').Double().ifNotExists().create(); |
| schema.vertexLabel('doublevertex2').properties('doublevalue2').ifNotExists().create(); |
| Double d2 = 3.5e40d; graph.addVertex(label, "doublevertex2", "doublevalue2", d2);'''] |
| |
| if DSE_VERSION >= Version('5.1'): |
| queries.append('''schema.propertyKey('datevalue1').Date().ifNotExists().create(); |
| schema.vertexLabel('datevertex1').properties('datevalue1').ifNotExists().create(); |
| schema.propertyKey('negdatevalue2').Date().ifNotExists().create(); |
| schema.vertexLabel('negdatevertex2').properties('negdatevalue2').ifNotExists().create();''') |
| |
| for i in range(1, 4): |
| queries.append('''schema.propertyKey('timevalue{0}').Time().ifNotExists().create(); |
| schema.vertexLabel('timevertex{0}').properties('timevalue{0}').ifNotExists().create();'''.format( |
| i)) |
| |
| queries.append('graph.addVertex(label, "datevertex1", "datevalue1", date1);') |
| query_params['date1'] = '1999-07-29' |
| |
| queries.append('graph.addVertex(label, "negdatevertex2", "negdatevalue2", date2);') |
| query_params['date2'] = '-1999-07-28' |
| |
| queries.append('graph.addVertex(label, "timevertex1", "timevalue1", time1);') |
| query_params['time1'] = '14:02' |
| queries.append('graph.addVertex(label, "timevertex2", "timevalue2", time2);') |
| query_params['time2'] = '14:02:20' |
| queries.append('graph.addVertex(label, "timevertex3", "timevalue3", time3);') |
| query_params['time3'] = '14:02:20.222' |
| |
| return queries, query_params |
| |
| @staticmethod |
| def large(): |
| query_parts = [''' |
| int size = 2000; |
| List ids = new ArrayList(); |
| schema.propertyKey('ts').Int().single().ifNotExists().create(); |
| schema.propertyKey('sin').Int().single().ifNotExists().create(); |
| schema.propertyKey('cos').Int().single().ifNotExists().create(); |
| schema.propertyKey('ii').Int().single().ifNotExists().create(); |
| schema.vertexLabel('lcg').properties('ts', 'sin', 'cos', 'ii').ifNotExists().create(); |
| schema.edgeLabel('linked').connection('lcg', 'lcg').ifNotExists().create(); |
| Vertex v = graph.addVertex(label, 'lcg'); |
| v.property("ts", 100001); |
| v.property("sin", 0); |
| v.property("cos", 1); |
| v.property("ii", 0); |
| ids.add(v.id()); |
| Random rand = new Random(); |
| for (int ii = 1; ii < size; ii++) { |
| v = graph.addVertex(label, 'lcg'); |
| v.property("ii", ii); |
| v.property("ts", 100001 + ii); |
| v.property("sin", Math.sin(ii/5.0)); |
| v.property("cos", Math.cos(ii/5.0)); |
| Vertex u = g.V(ids.get(rand.nextInt(ids.size()))).next(); |
| v.addEdge("linked", u); |
| ids.add(v.id()); |
| } |
| g.V().count();'''] |
| |
| return "\n".join(query_parts) |
| |
| @staticmethod |
| def address_book(): |
| p1 = "Point()" |
| p2 = "Point()" |
| if DSE_VERSION >= Version('5.1'): |
| p1 = "Point().withBounds(-100, -100, 100, 100)" |
| p2 = "Point().withGeoBounds()" |
| |
| queries = [ |
| ALLOW_SCANS, |
| "schema.propertyKey('name').Text().ifNotExists().create()", |
| "schema.propertyKey('pointPropWithBoundsWithSearchIndex').{}.ifNotExists().create()".format(p1), |
| "schema.propertyKey('pointPropWithBounds').{}.ifNotExists().create()".format(p1), |
| "schema.propertyKey('pointPropWithGeoBoundsWithSearchIndex').{}.ifNotExists().create()".format(p2), |
| "schema.propertyKey('pointPropWithGeoBounds').{}.ifNotExists().create()".format(p2), |
| "schema.propertyKey('city').Text().ifNotExists().create()", |
| "schema.propertyKey('state').Text().ifNotExists().create()", |
| "schema.propertyKey('description').Text().ifNotExists().create()", |
| "schema.vertexLabel('person').properties('name', 'city', 'state', 'description', 'pointPropWithBoundsWithSearchIndex', 'pointPropWithBounds', 'pointPropWithGeoBoundsWithSearchIndex', 'pointPropWithGeoBounds').ifNotExists().create()", |
| "schema.vertexLabel('person').index('searchPointWithBounds').secondary().by('pointPropWithBounds').ifNotExists().add()", |
| "schema.vertexLabel('person').index('searchPointWithGeoBounds').secondary().by('pointPropWithGeoBounds').ifNotExists().add()", |
| |
| "g.addV('person').property('name', 'Paul Thomas Joe').property('city', 'Rochester').property('state', 'MN').property('pointPropWithBoundsWithSearchIndex', Geo.point(-92.46295, 44.0234)).property('pointPropWithBounds', Geo.point(-92.46295, 44.0234)).property('pointPropWithGeoBoundsWithSearchIndex', Geo.point(-92.46295, 44.0234)).property('pointPropWithGeoBounds', Geo.point(-92.46295, 44.0234)).property('description', 'Lives by the hospital').next()", |
| "g.addV('person').property('name', 'George Bill Steve').property('city', 'Minneapolis').property('state', 'MN').property('pointPropWithBoundsWithSearchIndex', Geo.point(-93.266667, 44.093333)).property('pointPropWithBounds', Geo.point(-93.266667, 44.093333)).property('pointPropWithGeoBoundsWithSearchIndex', Geo.point(-93.266667, 44.093333)).property('pointPropWithGeoBounds', Geo.point(-93.266667, 44.093333)).property('description', 'A cold dude').next()", |
| "g.addV('person').property('name', 'James Paul Smith').property('city', 'Chicago').property('state', 'IL').property('pointPropWithBoundsWithSearchIndex', Geo.point(-87.684722, 41.836944)).property('description', 'Likes to hang out').next()", |
| "g.addV('person').property('name', 'Jill Alice').property('city', 'Atlanta').property('state', 'GA').property('pointPropWithBoundsWithSearchIndex', Geo.point(-84.39, 33.755)).property('description', 'Enjoys a nice cold coca cola').next()" |
| ] |
| |
| if not Version('5.0') <= DSE_VERSION < Version('5.1'): |
| queries.append("schema.vertexLabel('person').index('search').search().by('pointPropWithBoundsWithSearchIndex').withError(0.00001, 0.0).by('pointPropWithGeoBoundsWithSearchIndex').withError(0.00001, 0.0).ifNotExists().add()") |
| |
| return "\n".join(queries) |
| |
| |
| class CoreGraphFixtures(GraphFixtures): |
| |
| @staticmethod |
| def datatypes(): |
| data = ClassicGraphFixtures.datatypes() |
| del data['duration1'] |
| del data['duration2'] |
| |
| # Core Graphs only types |
| data["map1"] = ["mapOf(Text, Text)", {'test': 'test'}, None] |
| data["map2"] = ["mapOf(Text, Point)", {'test': Point(.5, .13)}, None] |
| data["map3"] = ["frozen(mapOf(Int, Varchar))", {42: 'test'}, None] |
| |
| data["list1"] = ["listOf(Text)", ['test', 'hello', 'world'], None] |
| data["list2"] = ["listOf(Int)", [42, 632, 32], None] |
| data["list3"] = ["listOf(Point)", [Point(.5, .13), Point(42.5, .13)], None] |
| data["list4"] = ["frozen(listOf(Int))", [42, 55, 33], None] |
| |
| data["set1"] = ["setOf(Text)", {'test', 'hello', 'world'}, None] |
| data["set2"] = ["setOf(Int)", {42, 632, 32}, None] |
| data["set3"] = ["setOf(Point)", {Point(.5, .13), Point(42.5, .13)}, None] |
| data["set4"] = ["frozen(setOf(Int))", {42, 55, 33}, None] |
| |
| data["tuple1"] = ["tupleOf(Int, Text)", (42, "world"), None] |
| data["tuple2"] = ["tupleOf(Int, tupleOf(Text, tupleOf(Text, Point)))", (42, ("world", ('this', Point(.5, .13)))), None] |
| data["tuple3"] = ["tupleOf(Int, tupleOf(Text, frozen(mapOf(Text, Text))))", (42, ("world", {'test': 'test'})), None] |
| data["tuple4"] = ["tupleOf(Int, tupleOf(Text, frozen(listOf(Int))))", (42, ("world", [65, 89])), None] |
| data["tuple5"] = ["tupleOf(Int, tupleOf(Text, frozen(setOf(Int))))", (42, ("world", {65, 55})), None] |
| data["tuple6"] = ["tupleOf(Int, tupleOf(Text, tupleOf(Text, LineString)))", |
| (42, ("world", ('this', LineString(((1.0, 2.0), (3.0, 4.0), (-89.0, 90.0)))))), None] |
| |
| data["tuple7"] = ["tupleOf(Int, tupleOf(Text, tupleOf(Text, Polygon)))", |
| (42, ("world", ('this', Polygon([(10.0, 10.0), (80.0, 10.0), (80., 88.0), (10., 89.0), (10., 10.0)], |
| [[(20., 20.0), (20., 30.0), (30., 30.0), (30., 20.0), (20., 20.0)], |
| [(40., 20.0), (40., 30.0), (50., 30.0), (50., 20.0), (40., 20.0)]])))), None] |
| data["dse_duration1"] = ["Duration()", Duration(42, 12, 10303312), None] |
| data["dse_duration2"] = ["Duration()", Duration(50, 32, 11), None] |
| |
| return data |
| |
| @staticmethod |
| def line(length, single_script=False): |
| queries = [""" |
| schema.vertexLabel('lp').ifNotExists().partitionBy('index', Int).create(); |
| schema.edgeLabel('goesTo').ifNotExists().from('lp').to('lp').property('distance', Int).create(); |
| """] |
| |
| vertex_script = ["g.addV('lp').property('index', 0).next();"] |
| for index in range(1, length): |
| if not single_script and len(vertex_script) > 25: |
| queries.append("\n".join(vertex_script)) |
| vertex_script = [] |
| |
| vertex_script.append(''' |
| g.addV('lp').property('index', {index}).next(); |
| g.V().hasLabel('lp').has('index', {pindex}).as('pp').V().hasLabel('lp').has('index', {index}).as('p'). |
| addE('goesTo').from('pp').to('p').property('distance', 5).next(); |
| '''.format( |
| index=index, pindex=index - 1)) |
| |
| queries.append("\n".join(vertex_script)) |
| return queries |
| |
| @staticmethod |
| def classic(): |
| queries = [ |
| ''' |
| schema.vertexLabel('person').ifNotExists().partitionBy('name', Text).property('age', Int).create(); |
| schema.vertexLabel('software')ifNotExists().partitionBy('name', Text).property('lang', Text).create(); |
| schema.edgeLabel('created').ifNotExists().from('person').to('software').property('weight', Double).create(); |
| schema.edgeLabel('knows').ifNotExists().from('person').to('person').property('weight', Double).create(); |
| ''', |
| |
| ''' |
| Vertex marko = g.addV('person').property('name', 'marko').property('age', 29).next(); |
| Vertex vadas = g.addV('person').property('name', 'vadas').property('age', 27).next(); |
| Vertex lop = g.addV('software').property('name', 'lop').property('lang', 'java').next(); |
| Vertex josh = g.addV('person').property('name', 'josh').property('age', 32).next(); |
| Vertex peter = g.addV('person').property('name', 'peter').property('age', 35).next(); |
| Vertex carl = g.addV('person').property('name', 'carl').property('age', 35).next(); |
| Vertex ripple = g.addV('software').property('name', 'ripple').property('lang', 'java').next(); |
| |
| // TODO, switch to VertexReference and use v.id() |
| g.V().hasLabel('person').has('name', 'vadas').as('v').V().hasLabel('person').has('name', 'marko').as('m').addE('knows').from('m').to('v').property('weight', 0.5d).next(); |
| g.V().hasLabel('person').has('name', 'josh').as('j').V().hasLabel('person').has('name', 'marko').as('m').addE('knows').from('m').to('j').property('weight', 1.0d).next(); |
| g.V().hasLabel('software').has('name', 'lop').as('l').V().hasLabel('person').has('name', 'marko').as('m').addE('created').from('m').to('l').property('weight', 0.4d).next(); |
| g.V().hasLabel('software').has('name', 'ripple').as('r').V().hasLabel('person').has('name', 'josh').as('j').addE('created').from('j').to('r').property('weight', 1.0d).next(); |
| g.V().hasLabel('software').has('name', 'lop').as('l').V().hasLabel('person').has('name', 'josh').as('j').addE('created').from('j').to('l').property('weight', 0.4d).next(); |
| g.V().hasLabel('software').has('name', 'lop').as('l').V().hasLabel('person').has('name', 'peter').as('p').addE('created').from('p').to('l').property('weight', 0.2d).next(); |
| |
| '''] |
| |
| return queries |
| |
| @staticmethod |
| def multiple_fields(): |
| ## no generic test currently needs this |
| raise NotImplementedError() |
| |
| @staticmethod |
| def large(): |
| query_parts = [ |
| ''' |
| schema.vertexLabel('lcg').ifNotExists().partitionBy('ts', Int).property('sin', Double). |
| property('cos', Double).property('ii', Int).create(); |
| schema.edgeLabel('linked').ifNotExists().from('lcg').to('lcg').create(); |
| ''', |
| |
| ''' |
| int size = 2000; |
| List ids = new ArrayList(); |
| v = g.addV('lcg').property('ts', 100001).property('sin', 0d).property('cos', 1d).property('ii', 0).next(); |
| ids.add(v.id()); |
| Random rand = new Random(); |
| for (int ii = 1; ii < size; ii++) { |
| v = g.addV('lcg').property('ts', 100001 + ii).property('sin', Math.sin(ii/5.0)).property('cos', Math.cos(ii/5.0)).property('ii', ii).next(); |
| |
| uid = ids.get(rand.nextInt(ids.size())) |
| g.V(v.id()).as('v').V(uid).as('u').addE('linked').from('v').to('u').next(); |
| ids.add(v.id()); |
| } |
| g.V().count();''' |
| ] |
| |
| return query_parts |
| |
| @staticmethod |
| def address_book(): |
| queries = [ |
| "schema.vertexLabel('person').ifNotExists().partitionBy('name', Text)." |
| "property('pointPropWithBoundsWithSearchIndex', Point)." |
| "property('pointPropWithBounds', Point)." |
| "property('pointPropWithGeoBoundsWithSearchIndex', Point)." |
| "property('pointPropWithGeoBounds', Point)." |
| "property('city', Text)." |
| "property('state', Text)." |
| "property('description', Text).create()", |
| "schema.vertexLabel('person').searchIndex().by('name').by('pointPropWithBounds').by('pointPropWithGeoBounds').by('description').asText().create()", |
| "g.addV('person').property('name', 'Paul Thomas Joe').property('city', 'Rochester').property('state', 'MN').property('pointPropWithBoundsWithSearchIndex', Geo.point(-92.46295, 44.0234)).property('pointPropWithBounds', Geo.point(-92.46295, 44.0234)).property('pointPropWithGeoBoundsWithSearchIndex', Geo.point(-92.46295, 44.0234)).property('pointPropWithGeoBounds', Geo.point(-92.46295, 44.0234)).property('description', 'Lives by the hospital').next()", |
| "g.addV('person').property('name', 'George Bill Steve').property('city', 'Minneapolis').property('state', 'MN').property('pointPropWithBoundsWithSearchIndex', Geo.point(-93.266667, 44.093333)).property('pointPropWithBounds', Geo.point(-93.266667, 44.093333)).property('pointPropWithGeoBoundsWithSearchIndex', Geo.point(-93.266667, 44.093333)).property('pointPropWithGeoBounds', Geo.point(-93.266667, 44.093333)).property('description', 'A cold dude').next()", |
| "g.addV('person').property('name', 'James Paul Smith').property('city', 'Chicago').property('state', 'IL').property('pointPropWithBoundsWithSearchIndex', Geo.point(-87.684722, 41.836944)).property('description', 'Likes to hang out').next()", |
| "g.addV('person').property('name', 'Jill Alice').property('city', 'Atlanta').property('state', 'GA').property('pointPropWithBoundsWithSearchIndex', Geo.point(-84.39, 33.755)).property('description', 'Enjoys a nice cold coca cola').next()" |
| ] |
| |
| if not Version('5.0') <= DSE_VERSION < Version('5.1'): |
| queries.append("schema.vertexLabel('person').searchIndex().by('pointPropWithBoundsWithSearchIndex').by('pointPropWithGeoBounds')" |
| ".by('pointPropWithGeoBoundsWithSearchIndex').create()") |
| |
| return queries |
| |
| |
| def validate_classic_vertex(test, vertex): |
| vertex_props = vertex.properties.keys() |
| test.assertEqual(len(vertex_props), 2) |
| test.assertIn('name', vertex_props) |
| test.assertTrue('lang' in vertex_props or 'age' in vertex_props) |
| |
| |
| def validate_classic_vertex_return_type(test, vertex): |
| validate_generic_vertex_result_type(vertex) |
| vertex_props = vertex.properties |
| test.assertIn('name', vertex_props) |
| test.assertTrue('lang' in vertex_props or 'age' in vertex_props) |
| |
| |
| def validate_generic_vertex_result_type(test, vertex): |
| test.assertIsInstance(vertex, Vertex) |
| for attr in ('id', 'type', 'label', 'properties'): |
| test.assertIsNotNone(getattr(vertex, attr)) |
| |
| |
| def validate_classic_edge_properties(test, edge_properties): |
| test.assertEqual(len(edge_properties.keys()), 1) |
| test.assertIn('weight', edge_properties) |
| test.assertIsInstance(edge_properties, dict) |
| |
| |
| def validate_classic_edge(test, edge): |
| validate_generic_edge_result_type(test, edge) |
| validate_classic_edge_properties(test, edge.properties) |
| |
| |
| def validate_line_edge(test, edge): |
| validate_generic_edge_result_type(test, edge) |
| edge_props = edge.properties |
| test.assertEqual(len(edge_props.keys()), 1) |
| test.assertIn('distance', edge_props) |
| |
| |
| def validate_generic_edge_result_type(test, edge): |
| test.assertIsInstance(edge, Edge) |
| for attr in ('properties', 'outV', 'outVLabel', 'inV', 'inVLabel', 'label', 'type', 'id'): |
| test.assertIsNotNone(getattr(edge, attr)) |
| |
| |
| def validate_path_result_type(test, path): |
| test.assertIsInstance(path, Path) |
| test.assertIsNotNone(path.labels) |
| for obj in path.objects: |
| if isinstance(obj, Edge): |
| validate_classic_edge(test, obj) |
| elif isinstance(obj, Vertex): |
| validate_classic_vertex(test, obj) |
| else: |
| test.fail("Invalid object found in path " + str(object.type)) |
| |
| |
| class GraphTestConfiguration(object): |
| """Possible Configurations: |
| ClassicGraphSchema: |
| graphson1 |
| graphson2 |
| graphson3 |
| |
| CoreGraphSchema |
| graphson3 |
| """ |
| |
| @classmethod |
| def schemas(cls): |
| schemas = [ClassicGraphSchema] |
| if DSE_VERSION >= Version("6.8"): |
| schemas.append(CoreGraphSchema) |
| return schemas |
| |
| @classmethod |
| def graphson_versions(cls): |
| graphson_versions = [GraphProtocol.GRAPHSON_1_0] |
| if DSE_VERSION >= Version("6.0"): |
| graphson_versions.append(GraphProtocol.GRAPHSON_2_0) |
| if DSE_VERSION >= Version("6.8"): |
| graphson_versions.append(GraphProtocol.GRAPHSON_3_0) |
| return graphson_versions |
| |
| @classmethod |
| def schema_configurations(cls, schema=None): |
| schemas = cls.schemas() if schema is None else [schema] |
| configurations = [] |
| for s in schemas: |
| configurations.append(s) |
| |
| return configurations |
| |
| @classmethod |
| def configurations(cls, schema=None, graphson=None): |
| schemas = cls.schemas() if schema is None else [schema] |
| graphson_versions = cls.graphson_versions() if graphson is None else [graphson] |
| |
| configurations = [] |
| for s in schemas: |
| for g in graphson_versions: |
| if s is CoreGraphSchema and g != GraphProtocol.GRAPHSON_3_0: |
| continue |
| configurations.append((s, g)) |
| |
| return configurations |
| |
| @staticmethod |
| def _make_graph_schema_test_method(func, schema): |
| def test_input(self): |
| self.setup_graph(schema) |
| try: |
| func(self, schema) |
| except: |
| raise |
| finally: |
| self.teardown_graph(schema) |
| |
| schema_name = 'classic' if schema is ClassicGraphSchema else 'core' |
| test_input.__name__ = '{func}_{schema}'.format( |
| func=func.__name__.lstrip('_'), schema=schema_name) |
| return test_input |
| |
| @staticmethod |
| def _make_graph_test_method(func, schema, graphson): |
| def test_input(self): |
| self.setup_graph(schema) |
| try: |
| func(self, schema, graphson) |
| except: |
| raise |
| finally: |
| self.teardown_graph(schema) |
| |
| graphson_name = 'graphson1' |
| if graphson == GraphProtocol.GRAPHSON_2_0: |
| graphson_name = 'graphson2' |
| elif graphson == GraphProtocol.GRAPHSON_3_0: |
| graphson_name = 'graphson3' |
| |
| schema_name = 'classic' if schema is ClassicGraphSchema else 'core' |
| |
| # avoid keyspace name too long issue |
| if DSE_VERSION < Version('6.7'): |
| schema_name = schema_name[0] |
| graphson_name = 'g' + graphson_name[-1] |
| |
| test_input.__name__ = '{func}_{schema}_{graphson}'.format( |
| func=func.__name__.lstrip('_'), schema=schema_name, graphson=graphson_name) |
| return test_input |
| |
| @classmethod |
| def generate_tests(cls, schema=None, graphson=None, traversal=False): |
| """Generate tests for a graph configuration""" |
| def decorator(klass): |
| if DSE_VERSION: |
| predicate = inspect.isfunction |
| for name, func in inspect.getmembers(klass, predicate=predicate): |
| if not name.startswith('_test'): |
| continue |
| for _schema, _graphson in cls.configurations(schema, graphson): |
| if traversal and _graphson == GraphProtocol.GRAPHSON_1_0: |
| continue |
| test_input = cls._make_graph_test_method(func, _schema, _graphson) |
| log.debug("Generated test '{}.{}'".format(klass.__name__, test_input.__name__)) |
| setattr(klass, test_input.__name__, test_input) |
| return klass |
| |
| return decorator |
| |
| @classmethod |
| def generate_schema_tests(cls, schema=None): |
| """Generate schema tests for a graph configuration""" |
| def decorator(klass): |
| if DSE_VERSION: |
| predicate = inspect.isfunction |
| for name, func in inspect.getmembers(klass, predicate=predicate): |
| if not name.startswith('_test'): |
| continue |
| for _schema in cls.schema_configurations(schema): |
| test_input = cls._make_graph_schema_test_method(func, _schema) |
| log.debug("Generated test '{}.{}'".format(klass.__name__, test_input.__name__)) |
| setattr(klass, test_input.__name__, test_input) |
| return klass |
| |
| return decorator |
| |
| |
| class VertexLabel(object): |
| """ |
| Helper that represents a new VertexLabel: |
| |
| VertexLabel(['Int()', 'Float()']) # a vertex with 2 properties named property1 and property2 |
| VertexLabel([('int1', 'Int()'), 'Float()']) # a vertex with 2 properties named int1 and property1 |
| """ |
| |
| id = 0 |
| label = None |
| properties = None |
| |
| def __init__(self, properties): |
| VertexLabel.id += 1 |
| self.id = VertexLabel.id |
| self.label = "vertex{}".format(self.id) |
| self.properties = {'pkid': self.id} |
| property_count = 0 |
| for p in properties: |
| if isinstance(p, tuple): |
| name, typ = p |
| else: |
| property_count += 1 |
| name = "property-v{}-{}".format(self.id, property_count) |
| typ = p |
| self.properties[name] = typ |
| |
| @property |
| def non_pk_properties(self): |
| return {p: v for p, v in self.properties.items() if p != 'pkid'} |
| |
| |
| class GraphSchema(object): |
| |
| has_geo_bounds = DSE_VERSION and DSE_VERSION >= Version('5.1') |
| fixtures = GraphFixtures |
| |
| @classmethod |
| def sanitize_type(cls, typ): |
| if typ.lower().startswith("point"): |
| return cls.sanitize_point_type() |
| elif typ.lower().startswith("line"): |
| return cls.sanitize_line_type() |
| elif typ.lower().startswith("poly"): |
| return cls.sanitize_polygon_type() |
| else: |
| return typ |
| |
| @classmethod |
| def sanitize_point_type(cls): |
| return "Point().withGeoBounds()" if cls.has_geo_bounds else "Point()" |
| |
| @classmethod |
| def sanitize_line_type(cls): |
| return "Linestring().withGeoBounds()" if cls.has_geo_bounds else "Linestring()" |
| |
| @classmethod |
| def sanitize_polygon_type(cls): |
| return "Polygon().withGeoBounds()" if cls.has_geo_bounds else "Polygon()" |
| |
| @staticmethod |
| def drop_graph(session, graph_name): |
| ks = list(session.execute( |
| "SELECT * FROM system_schema.keyspaces WHERE keyspace_name = '{}';".format(graph_name))) |
| if not ks: |
| return |
| |
| try: |
| session.execute_graph('system.graph(name).drop()', {'name': graph_name}, |
| execution_profile=EXEC_PROFILE_GRAPH_SYSTEM_DEFAULT) |
| except: |
| pass |
| |
| @staticmethod |
| def create_graph(session, graph_name): |
| raise NotImplementedError() |
| |
| @staticmethod |
| def clear(session): |
| pass |
| |
| @staticmethod |
| def create_vertex_label(session, vertex_label, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT): |
| raise NotImplementedError() |
| |
| @staticmethod |
| def add_vertex(session, vertex_label, name, value, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT): |
| raise NotImplementedError() |
| |
| @classmethod |
| def ensure_properties(cls, session, obj, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT): |
| if not isinstance(obj, (Vertex, Edge)): |
| return |
| |
| # This pre-processing is due to a change in TinkerPop |
| # properties are not returned automatically anymore |
| # with some queries. |
| if not obj.properties: |
| if isinstance(obj, Edge): |
| obj.properties = {} |
| for p in cls.get_edge_properties(session, obj, execution_profile=execution_profile): |
| obj.properties.update(p) |
| elif isinstance(obj, Vertex): |
| obj.properties = { |
| p.label: p |
| for p in cls.get_vertex_properties(session, obj, execution_profile=execution_profile) |
| } |
| |
| @staticmethod |
| def get_vertex_properties(session, vertex, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT): |
| return session.execute_graph("g.V(vertex_id).properties().toList()", {'vertex_id': vertex.id}, |
| execution_profile=execution_profile) |
| |
| @staticmethod |
| def get_edge_properties(session, edge, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT): |
| v = session.execute_graph("g.E(edge_id).properties().toList()", {'edge_id': edge.id}, |
| execution_profile=execution_profile) |
| return v |
| |
| |
| class ClassicGraphSchema(GraphSchema): |
| |
| fixtures = ClassicGraphFixtures |
| |
| @staticmethod |
| def create_graph(session, graph_name): |
| session.execute_graph(CREATE_CLASSIC_GRAPH, {'name': graph_name}, |
| execution_profile=EXEC_PROFILE_GRAPH_SYSTEM_DEFAULT) |
| wait_for_graph_inserted(session, graph_name) |
| |
| @staticmethod |
| def clear(session): |
| session.execute_graph('schema.clear()') |
| |
| @classmethod |
| def create_vertex_label(cls, session, vertex_label, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT): |
| statements = ["schema.propertyKey('pkid').Int().ifNotExists().create();"] |
| for k, v in vertex_label.non_pk_properties.items(): |
| typ = cls.sanitize_type(v) |
| statements.append("schema.propertyKey('{name}').{type}.create();".format( |
| name=k, type=typ |
| )) |
| |
| statements.append("schema.vertexLabel('{label}').partitionKey('pkid').properties(".format( |
| label=vertex_label.label)) |
| property_names = [name for name in vertex_label.non_pk_properties.keys()] |
| statements.append(", ".join(["'{}'".format(p) for p in property_names])) |
| statements.append(").create();") |
| |
| to_run = "\n".join(statements) |
| session.execute_graph(to_run, execution_profile=execution_profile) |
| |
| @staticmethod |
| def add_vertex(session, vertex_label, name, value, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT): |
| statement = "g.addV('{label}').property('pkid', {pkid}).property('{property_name}', val);".format( |
| pkid=vertex_label.id, label=vertex_label.label, property_name=name) |
| parameters = {'val': value} |
| return session.execute_graph(statement, parameters, execution_profile=execution_profile) |
| |
| |
| class CoreGraphSchema(GraphSchema): |
| |
| fixtures = CoreGraphFixtures |
| |
| @classmethod |
| def sanitize_type(cls, typ): |
| typ = super(CoreGraphSchema, cls).sanitize_type(typ) |
| return typ.replace('()', '') |
| |
| @classmethod |
| def sanitize_point_type(cls): |
| return "Point" |
| |
| @classmethod |
| def sanitize_line_type(cls): |
| return "LineString" |
| |
| @classmethod |
| def sanitize_polygon_type(cls): |
| return "Polygon" |
| |
| @staticmethod |
| def create_graph(session, graph_name): |
| session.execute_graph('system.graph(name).create()', {'name': graph_name}, |
| execution_profile=EXEC_PROFILE_GRAPH_SYSTEM_DEFAULT) |
| wait_for_graph_inserted(session, graph_name) |
| |
| @classmethod |
| def create_vertex_label(cls, session, vertex_label, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT): |
| statements = ["schema.vertexLabel('{label}').partitionBy('pkid', Int)".format( |
| label=vertex_label.label)] |
| |
| for name, typ in vertex_label.non_pk_properties.items(): |
| typ = cls.sanitize_type(typ) |
| statements.append(".property('{name}', {type})".format(name=name, type=typ)) |
| statements.append(".create();") |
| |
| to_run = "\n".join(statements) |
| session.execute_graph(to_run, execution_profile=execution_profile) |
| |
| @staticmethod |
| def add_vertex(session, vertex_label, name, value, execution_profile=EXEC_PROFILE_GRAPH_DEFAULT): |
| statement = "g.addV('{label}').property('pkid', {pkid}).property('{property_name}', val);".format( |
| pkid=vertex_label.id, label=vertex_label.label, property_name=name) |
| parameters = {'val': value} |
| return session.execute_graph(statement, parameters, execution_profile=execution_profile) |