图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

263 lines
7.1 KiB

  1. import warnings
  2. from ..helpers import quote_string, random_string, stringify_param_value
  3. from .commands import AsyncGraphCommands, GraphCommands
  4. from .edge import Edge # noqa
  5. from .node import Node # noqa
  6. from .path import Path # noqa
  7. DB_LABELS = "DB.LABELS"
  8. DB_RAELATIONSHIPTYPES = "DB.RELATIONSHIPTYPES"
  9. DB_PROPERTYKEYS = "DB.PROPERTYKEYS"
  10. class Graph(GraphCommands):
  11. """
  12. Graph, collection of nodes and edges.
  13. """
  14. def __init__(self, client, name=random_string()):
  15. """
  16. Create a new graph.
  17. """
  18. warnings.warn(
  19. DeprecationWarning(
  20. "RedisGraph support is deprecated as of Redis Stack 7.2 \
  21. (https://redis.com/blog/redisgraph-eol/)"
  22. )
  23. )
  24. self.NAME = name # Graph key
  25. self.client = client
  26. self.execute_command = client.execute_command
  27. self.nodes = {}
  28. self.edges = []
  29. self._labels = [] # List of node labels.
  30. self._properties = [] # List of properties.
  31. self._relationship_types = [] # List of relation types.
  32. self.version = 0 # Graph version
  33. @property
  34. def name(self):
  35. return self.NAME
  36. def _clear_schema(self):
  37. self._labels = []
  38. self._properties = []
  39. self._relationship_types = []
  40. def _refresh_schema(self):
  41. self._clear_schema()
  42. self._refresh_labels()
  43. self._refresh_relations()
  44. self._refresh_attributes()
  45. def _refresh_labels(self):
  46. lbls = self.labels()
  47. # Unpack data.
  48. self._labels = [l[0] for _, l in enumerate(lbls)]
  49. def _refresh_relations(self):
  50. rels = self.relationship_types()
  51. # Unpack data.
  52. self._relationship_types = [r[0] for _, r in enumerate(rels)]
  53. def _refresh_attributes(self):
  54. props = self.property_keys()
  55. # Unpack data.
  56. self._properties = [p[0] for _, p in enumerate(props)]
  57. def get_label(self, idx):
  58. """
  59. Returns a label by it's index
  60. Args:
  61. idx:
  62. The index of the label
  63. """
  64. try:
  65. label = self._labels[idx]
  66. except IndexError:
  67. # Refresh labels.
  68. self._refresh_labels()
  69. label = self._labels[idx]
  70. return label
  71. def get_relation(self, idx):
  72. """
  73. Returns a relationship type by it's index
  74. Args:
  75. idx:
  76. The index of the relation
  77. """
  78. try:
  79. relationship_type = self._relationship_types[idx]
  80. except IndexError:
  81. # Refresh relationship types.
  82. self._refresh_relations()
  83. relationship_type = self._relationship_types[idx]
  84. return relationship_type
  85. def get_property(self, idx):
  86. """
  87. Returns a property by it's index
  88. Args:
  89. idx:
  90. The index of the property
  91. """
  92. try:
  93. p = self._properties[idx]
  94. except IndexError:
  95. # Refresh properties.
  96. self._refresh_attributes()
  97. p = self._properties[idx]
  98. return p
  99. def add_node(self, node):
  100. """
  101. Adds a node to the graph.
  102. """
  103. if node.alias is None:
  104. node.alias = random_string()
  105. self.nodes[node.alias] = node
  106. def add_edge(self, edge):
  107. """
  108. Adds an edge to the graph.
  109. """
  110. if not (self.nodes[edge.src_node.alias] and self.nodes[edge.dest_node.alias]):
  111. raise AssertionError("Both edge's end must be in the graph")
  112. self.edges.append(edge)
  113. def _build_params_header(self, params):
  114. if params is None:
  115. return ""
  116. if not isinstance(params, dict):
  117. raise TypeError("'params' must be a dict")
  118. # Header starts with "CYPHER"
  119. params_header = "CYPHER "
  120. for key, value in params.items():
  121. params_header += str(key) + "=" + stringify_param_value(value) + " "
  122. return params_header
  123. # Procedures.
  124. def call_procedure(self, procedure, *args, read_only=False, **kwagrs):
  125. args = [quote_string(arg) for arg in args]
  126. q = f"CALL {procedure}({','.join(args)})"
  127. y = kwagrs.get("y", None)
  128. if y is not None:
  129. q += f"YIELD {','.join(y)}"
  130. return self.query(q, read_only=read_only)
  131. def labels(self):
  132. return self.call_procedure(DB_LABELS, read_only=True).result_set
  133. def relationship_types(self):
  134. return self.call_procedure(DB_RAELATIONSHIPTYPES, read_only=True).result_set
  135. def property_keys(self):
  136. return self.call_procedure(DB_PROPERTYKEYS, read_only=True).result_set
  137. class AsyncGraph(Graph, AsyncGraphCommands):
  138. """Async version for Graph"""
  139. async def _refresh_labels(self):
  140. lbls = await self.labels()
  141. # Unpack data.
  142. self._labels = [l[0] for _, l in enumerate(lbls)]
  143. async def _refresh_attributes(self):
  144. props = await self.property_keys()
  145. # Unpack data.
  146. self._properties = [p[0] for _, p in enumerate(props)]
  147. async def _refresh_relations(self):
  148. rels = await self.relationship_types()
  149. # Unpack data.
  150. self._relationship_types = [r[0] for _, r in enumerate(rels)]
  151. async def get_label(self, idx):
  152. """
  153. Returns a label by it's index
  154. Args:
  155. idx:
  156. The index of the label
  157. """
  158. try:
  159. label = self._labels[idx]
  160. except IndexError:
  161. # Refresh labels.
  162. await self._refresh_labels()
  163. label = self._labels[idx]
  164. return label
  165. async def get_property(self, idx):
  166. """
  167. Returns a property by it's index
  168. Args:
  169. idx:
  170. The index of the property
  171. """
  172. try:
  173. p = self._properties[idx]
  174. except IndexError:
  175. # Refresh properties.
  176. await self._refresh_attributes()
  177. p = self._properties[idx]
  178. return p
  179. async def get_relation(self, idx):
  180. """
  181. Returns a relationship type by it's index
  182. Args:
  183. idx:
  184. The index of the relation
  185. """
  186. try:
  187. relationship_type = self._relationship_types[idx]
  188. except IndexError:
  189. # Refresh relationship types.
  190. await self._refresh_relations()
  191. relationship_type = self._relationship_types[idx]
  192. return relationship_type
  193. async def call_procedure(self, procedure, *args, read_only=False, **kwagrs):
  194. args = [quote_string(arg) for arg in args]
  195. q = f"CALL {procedure}({','.join(args)})"
  196. y = kwagrs.get("y", None)
  197. if y is not None:
  198. f"YIELD {','.join(y)}"
  199. return await self.query(q, read_only=read_only)
  200. async def labels(self):
  201. return ((await self.call_procedure(DB_LABELS, read_only=True))).result_set
  202. async def property_keys(self):
  203. return (await self.call_procedure(DB_PROPERTYKEYS, read_only=True)).result_set
  204. async def relationship_types(self):
  205. return (
  206. await self.call_procedure(DB_RAELATIONSHIPTYPES, read_only=True)
  207. ).result_set