def query_entity(self): nmatcher = NodeMatcher(self.graph) rmatcher = RelationshipMatcher(self.graph) # 大通有哪些车 a_node = nmatcher.match('品牌', name='上汽大通').first() b_node = nmatcher.match('车系').first() relation = rmatcher.match({a_node, b_node}).first() zz = self.graph.match((a_node, ), r_type=type(relation).__name__).all() result = [] for z in zz: result.append(z.end_node.get('name')) print('大通具有:') print(result) # nodes = rmatcher.match({a_node, b_node}).all() # print(nodes[0]) # print(nodes[0].start_node) # print(nodes[0].end_node) # print(nodes[0].nodes) # print(type(nodes[0]).__name__) # # A(实体)车的基本参数(实体) a_node = nmatcher.match('车型', name='2019款 2.0T柴油自动四驱精英型长厢高底盘').first() b_node = nmatcher.match('基本参数').first() relation = rmatcher.match({a_node, b_node}).first() print(relation) print(type(relation).__name__) print(dict(nmatcher.match(name='2019款 2.0T柴油自动四驱精英型长厢高底盘').first())) zz = self.graph.match(nodes=(relation.start_node, ), r_type=type(relation).__name__).all()[0].end_node print(zz)
def relationships(self): """ A :class:`.RelationshipMatcher` for this graph. This can be used to find relationships that match given criteria as well as efficiently count relationships. """ return RelationshipMatcher(self)
def match(): """这里的节点是正常的,它有两个属性name和age name是Liz age是34 match("Person").where(age=34).first() 正常 match("Person").where(name='Liz').first() 正常 match("Person", name="Liz").first() 正常 match("Person", age=34).first() 正常 match("Person", age=34).where(name="Liz").first() None match("Person", name="Liz").where(age=34).first() None """ matcher_1 = NodeMatcher(graph) matcher_2 = RelationshipMatcher(graph) # TODO: 这里的 age 属性使用后返回结果为 None node = matcher_1.match("Person", name="Liz").where(age=34).first() relation = matcher_2.match(r_type='FRIENDS') return list(relation), node, type(relation)
def query_test(self): nmatcher = NodeMatcher(self.graph) rmatcher = RelationshipMatcher(self.graph) # A车的基本参数 a_node = nmatcher.match('车型', name='2019款 2.0T柴油自动四驱精英型长厢高底盘').first() # relation = rmatcher.match({a_node, b_node}).first() relation = self.graph.match(nodes=(a_node, ), r_type='基本参数').all() print(dict(relation[0].end_node)) # gg = self.graph.match(nodes=(relation.start_node, relation.end_node)).all() # b_node = nmatcher.match('车型基本参数', name='基本参数节点').first()
def match(self, nodes=None, r_type=None, limit=None): """ Match and return all relationships with specific criteria. For example, to find all of Alice's friends:: for rel in graph.match((alice, ), r_type="FRIEND"): print(rel.end_node["name"]) :param nodes: Sequence or Set of start and end nodes (:const:`None` means any node); a Set implies a match in any direction :param r_type: type of relationships to match (:const:`None` means any type) :param limit: maximum number of relationships to match (:const:`None` means unlimited) """ return RelationshipMatcher(self).match(nodes=nodes, r_type=r_type).limit(limit)
def hydrate_object(obj, inst=None): from py2neo.data import Node, Relationship, Path from py2neo.client.packstream import Structure if isinstance(obj, Structure): tag = obj.tag fields = obj.fields if tag == ord(b"N"): return Node.hydrate(self.graph, fields[0], fields[1], hydrate_object(fields[2]), into=inst) elif tag == ord(b"R"): return Relationship.hydrate(self.graph, fields[0], fields[1], fields[2], fields[3], hydrate_object(fields[4]), into=inst) elif tag == ord(b"P"): # Herein lies a dirty hack to retrieve missing relationship # detail for paths received over HTTP. nodes = [hydrate_object(node) for node in fields[0]] u_rels = [] typeless_u_rel_ids = [] for r in fields[1]: u_rel = self.unbound_relationship(*map(hydrate_object, r)) assert u_rel.type is None typeless_u_rel_ids.append(u_rel.id) u_rels.append(u_rel) if typeless_u_rel_ids: r_dict = {r.identity: r for r in RelationshipMatcher(graph).get(typeless_u_rel_ids)} for i, u_rel in enumerate(u_rels): if u_rel.type is None: u_rels[i] = self.unbound_relationship( u_rel.id, type(r_dict[u_rel.id]).__name__, u_rel.properties ) sequence = fields[2] return Path.hydrate(self.graph, nodes, u_rels, sequence) else: try: f = self.hydration_functions[tag] except KeyError: # If we don't recognise the structure type, just return it as-is return obj else: return f(*map(hydrate_object, obj.fields)) elif isinstance(obj, list): return list(map(hydrate_object, obj)) elif isinstance(obj, dict): return {key: hydrate_object(value) for key, value in obj.items()} else: return obj
def hydrate_object(self, obj): log.debug("Hydrating object %r", obj) from py2neo.data import Node, Relationship, Path if isinstance(obj, Structure): tag = obj.tag fields = obj.fields if tag == ord(b"N"): obj = Node.ref(self.graph, fields[0]) if fields[1] is not None: obj._remote_labels = frozenset(fields[1]) obj.clear_labels() obj.update_labels(fields[1]) if fields[2] is not None: obj.clear() obj.update(self.hydrate_object(fields[2])) return obj elif tag == ord(b"R"): start_node = Node.ref(self.graph, fields[1]) end_node = Node.ref(self.graph, fields[2]) obj = Relationship.ref(self.graph, fields[0], start_node, fields[3], end_node) if fields[4] is not None: obj.clear() obj.update(self.hydrate_object(fields[4])) return obj elif tag == ord(b"P"): # Herein lies a dirty hack to retrieve missing relationship # detail for paths received over HTTP. nodes = [self.hydrate_object(node) for node in fields[0]] u_rels = [] typeless_u_rel_ids = [] for r in fields[1]: u_rel = self.unbound_relationship( *map(self.hydrate_object, r)) assert u_rel.type is None typeless_u_rel_ids.append(u_rel.id) u_rels.append(u_rel) if typeless_u_rel_ids: r_dict = { r.identity: r for r in RelationshipMatcher(self.graph).get( typeless_u_rel_ids) } for i, u_rel in enumerate(u_rels): if u_rel.type is None: u_rels[i] = self.unbound_relationship( u_rel.id, type(r_dict[u_rel.id]).__name__, u_rel.properties) sequence = fields[2] return Path.hydrate(self.graph, nodes, u_rels, sequence) else: try: f = self.hydration_functions[tag] except KeyError: # If we don't recognise the structure type, just return it as-is return obj else: return f(*map(self.hydrate_object, obj.fields)) elif isinstance(obj, list): return list(map(self.hydrate_object, obj)) elif isinstance(obj, dict): return { key: self.hydrate_object(value) for key, value in obj.items() } else: return obj
# coding:utf-8 from py2neo import Graph, Node, data, Path, Relationship from py2neo.matching import \ NodeMatcher, RelationshipMatcher, \ EQ, NE, LT, LE, GT, GE, \ STARTS_WITH, ENDS_WITH, CONTAINS, LIKE, \ IN, AND, OR, XOR import pandas as pd graph = Graph('http://localhost:7474', username='******', password='******') graphMather = RelationshipMatcher(graph=graph) data = pd.read_excel('./data/test3.xlsx', index_col=0) for index in data.index: content = data.loc[index].dropna() #### 创建车系节点 ########################################################## car_series_node = graph.nodes.match('车系', name=content['车系1']).first() if car_series_node == None: car_series_node = Node('车系', name=content['车系1']) graph.create(car_series_node) ############################################################## #### 创建车型节点 ########################################### car_model_node = Node('车型', name=content['车型']) graph.create(car_model_node) ############################################### #### 创建车系车型边######################################
def hydrate_object(obj, inst=None): from py2neo.data import Path if isinstance(obj, Structure): tag = obj.tag fields = obj.fields if tag == b"N": return self.hydrate_node(inst, fields[0], fields[1], hydrate_object(fields[2])) elif tag == b"R": return self.hydrate_relationship(inst, fields[0], fields[1], fields[2], fields[3], hydrate_object(fields[4])) elif tag == b"P": # Herein lies a dirty hack to retrieve missing relationship # detail for paths received over HTTP. nodes = [hydrate_object(node) for node in fields[0]] u_rels = [] typeless_u_rel_ids = [] for r in fields[1]: u_rel = self.unbound_relationship(*map(hydrate_object, r)) assert u_rel.type is None typeless_u_rel_ids.append(u_rel.id) u_rels.append(u_rel) if typeless_u_rel_ids: r_dict = {r.identity: r for r in RelationshipMatcher(graph).get(typeless_u_rel_ids)} for i, u_rel in enumerate(u_rels): if u_rel.type is None: u_rels[i] = self.unbound_relationship( u_rel.id, type(r_dict[u_rel.id]).__name__, u_rel.properties ) sequence = fields[2] last_node = nodes[0] steps = [last_node] for i, rel_index in enumerate(sequence[::2]): next_node = nodes[sequence[2 * i + 1]] if rel_index > 0: u_rel = u_rels[rel_index - 1] rel = self.hydrate_relationship(None, u_rel.id, last_node.identity, next_node.identity, u_rel.type, u_rel.properties) else: u_rel = u_rels[-rel_index - 1] rel = self.hydrate_relationship(None, u_rel.id, next_node.identity, last_node.identity, u_rel.type, u_rel.properties) steps.append(rel) # steps.append(next_node) last_node = next_node return Path(*steps) else: try: f = self.hydration_functions[obj.tag] except KeyError: # If we don't recognise the structure type, just return it as-is return obj else: return f(*map(hydrate_object, obj.fields)) elif isinstance(obj, list): return list(map(hydrate_object, obj)) elif isinstance(obj, dict): return {key: hydrate_object(value) for key, value in obj.items()} else: return obj
def __init__(self): self.tx = g.begin() self.schema = Schema(g) self.n_matcher = NodeMatcher(g) self.r_matcher = RelationshipMatcher(g)
def __post_init__(self): self.nmatcher = NodeMatcher(self.graph) self.rmatcher = RelationshipMatcher(self.graph)
class NLMGraph: """ The Memory Graph. Parameters ----------- graph: Graph The Neo4j Graph instance. """ graph: Graph def __post_init__(self): self.nmatcher = NodeMatcher(self.graph) self.rmatcher = RelationshipMatcher(self.graph) @raise_customized_error(Exception, DatabaseError) def push_graph(self, subgraph: Subgraph) -> bool: """ Push a subgraph (node, relationship, subgraph) to the Neo database. """ tx = self.graph.begin() tx.create(subgraph) tx.commit() return tx.finished() def add_node(self, label: str, name: str, props: dict) -> Node: """ Add a Node to database. Parameters ------------ label: Node label name: Node name props: Node property Returns -------- out: a Node. """ node = Node(label, name=name, **props) self.push_graph(node) return node def check_update_node(self, nlmgn: GraphNode, update_props: bool = False) -> Node: """ Check whether the given node is already in the graph. Parameters ------------ nlmgn: GraphNode The defined Node data type. Includes name, labels and properties. Returns -------- out: Node Whether it is already in the graph. If is, update with the new properties, if necessary and return the updated node. If not, return the created Node (and need to commit to the graph). """ label, name, props = nlmgn.label, nlmgn.name, nlmgn.props neogn = self.nmatcher.match(label, name=name).first() if neogn: if update_props: node = self.update_property(neogn, props) else: node = neogn else: node = self.add_node(label, name, props) return node @raise_customized_error(Exception, DatabaseError) def update_property(self, neog_oj, props: dict): """ Update a neo graph node or relationship. Parameters ------------ neog_oj: neo graph object, Node or Relationship Returns -------- out: updated Node or Relationship """ neog_oj_props = dict(neog_oj) if props and props != neog_oj_props: # make sure new props is behind the exisited props. neog_oj.update({**neog_oj_props, **props}) # only can be pushed when neog_oj is already in the graph # so we do not need push_graph function here self.graph.push(neog_oj) return neog_oj def add_relationship(self, start: Node, end: Node, kind: str, props: dict) -> Relationship: """ Add a Relationship to database. Parameters ------------ start: start Node end: end Node kind: Relationship kind props: Relationship property Returns -------- out: a Relationship. """ relation = Relationship(start, kind, end, **props) self.push_graph(relation) return relation def check_update_relationship(self, nlmgr: GraphRelation, update_props: bool = False) -> Relationship: """ Parameters ------------ nlmgr: GraphRelation The defined Relationship data type. Includes kind, start, end and properties. Returns -------- out: Relationship """ kind, props = nlmgr.kind, nlmgr.props start = self.check_update_node(nlmgr.start, update_props) end = self.check_update_node(nlmgr.end, update_props) neogr = self.rmatcher.match((start, end), r_type=kind).first() if neogr: if update_props: relation = self.update_property(neogr, props) else: relation = neogr else: relation = self.add_relationship(start, end, kind, props) return relation def add(self, gin: GraphRelation or GraphNode) -> Node or List[Node] or Relationship: """ Add a Node or Relationship to the database. Parameters ------------ gin: A GraphNode or GraphRelation (kind could be None) Returns -------- out: A Node or Relationship. """ if isinstance(gin, GraphNode): return self.add_node(gin.label, gin.name, gin.props) elif isinstance(gin, GraphRelation) and gin.kind: start = self.check_update_node(gin.start) end = self.check_update_node(gin.end) return self.add_relationship(start, end, gin.kind, gin.props) elif isinstance(gin, GraphRelation) and gin.kind == None: start = self.check_update_node(gin.start) end = self.check_update_node(gin.end) return (start, end) else: raise InputError def update( self, gin: GraphRelation or GraphNode) -> Node or List[Node] or Relationship: """ Update the property of a Node or Relationship to the database. Parameters ------------ gin: A GraphNode or GraphRelation (kind could be None) Returns -------- out: A Node or Relationship. """ if isinstance(gin, GraphNode): return self.check_update_node(gin, update_props=True) elif isinstance(gin, GraphRelation) and gin.kind: return self.check_update_relationship(gin, update_props=True) elif isinstance(gin, GraphRelation) and gin.kind == None: start = self.check_update_node(gin.start, update_props=True) end = self.check_update_node(gin.end, update_props=True) return (start, end) else: raise InputError def query(self, qin, topn=1, limit=10, fuzzy=False) -> list: """ Query by user given. Parameters ----------- qin: could be GraphNode, GraphRelation, or just Cypher. Returns --------- out: queried Nodes or Relationships. """ if isinstance(qin, GraphNode): ret = self._query_by_node(qin, topn, limit, fuzzy) elif isinstance(qin, GraphRelation): ret = self._query_by_relation(qin, topn, limit, fuzzy) elif isinstance(qin, str): ret = self._query_by_cypher(qin) else: raise InputError return ret def _sort_matched(self, matched_nodes: list, props: dict) -> list: """ Sort matched nodes by comparing their properties with the given props. """ ret = [] for node in matched_nodes: nprops = dict(node) num = 0 for k, v in props.items(): if k in nprops and nprops[k] == v: num += 1 ret.append((node, num)) sorted_ret = sorted(ret, key=lambda x: x[1], reverse=True) return [n for (n, _) in sorted_ret] @raise_customized_error(Exception, QueryError) def _query_by_node(self, gn: GraphNode, topn: int, limit: int, fuzzy: bool) -> List[Node]: """ Query node by given label and name. If None, then by those nodes whose nodes contains the given name """ label, name, props = gn.label, gn.name, gn.props nmatch = self.nmatcher.match(label) nodes = nmatch.where(name=name).limit(limit) if fuzzy and nodes.first() == None: nodes = nmatch.where(name__contains=name).limit(limit) nmlst = list(nodes) return self.__from_match_to_return(nmlst, props, topn) @raise_customized_error(Exception, QueryError) def _query_by_relation(self, gr: GraphRelation, topn: int, limit: int, fuzzy: bool) -> List[Relationship]: """ Query relations by given start, end and kind. If start and end are None, return []. If start or end is None, then by kind and start or end. If result is None, then by start or end, or by both. """ starts = self._query_by_node(gr.start, topn=1, limit=5, fuzzy=fuzzy) ends = self._query_by_node(gr.end, topn=1, limit=5, fuzzy=fuzzy) start = starts[0] if starts else None end = ends[0] if ends else None kind, props = gr.kind, gr.props # print("start: ", start) # print("end:", end) if not start and not end: return [] # start, end could be None # r_type could be None relations = self.rmatcher.match((start, end), r_type=kind).limit(limit) if relations.first() == None and start and end: relations = self.rmatcher.match((start, end)).limit(limit) rmlst = list(relations) return self.__from_match_to_return(rmlst, props, topn) def _query_by_cypher(self, cypher: str) -> types.GeneratorType: """ Return a generator, the content depends on your query input. """ pattern_match = re.compile(r'^ ?MATCH') pattern_limit = re.compile(r'LIMIT \d') if not pattern_match.search(cypher): raise OverstepError searched_limit = pattern_limit.search(cypher) topn = int(searched_limit.group().split()[-1]) if searched_limit else 5 try: cursor = self.graph.run(cypher) res = [] n = 0 for item in cursor: res.append(item.data()) n += 1 if n == topn: return res except Exception as e: raise QueryError def __from_match_to_return(self, matched_list: list, props: dict, topn: int) -> list: if not matched_list: return [] if len(matched_list) > 1 and props: ret = self._sort_matched(matched_list, props)[:topn] else: ret = matched_list[:topn] return ret @property def labels(self) -> frozenset: """all labels""" return self.graph.schema.node_labels @property def relationship_types(self) -> frozenset: """all relation types""" return self.graph.schema.relationship_types @property def nodes_num(self) -> int: """all nodes amounts""" return len(self.graph.nodes) @property def relationships_num(self) -> int: """all relations amounts""" return len(self.graph.relationships) @property def nodes(self) -> types.GeneratorType: """all nodes (a generator)""" return iter(self.graph.nodes.match()) @property def relationships(self) -> types.GeneratorType: """all relations (a generator)""" return iter(self.graph.relationships.match()) def excute(self, cypher) -> dict: """ Be careful to use this function. Especially when you're updating the database. This function will not check the duplicated nodes or relationships. """ try: run = self.graph.run(cypher) return dict(run.stats()) except Exception as e: raise InputError