def init_workmate_cost(): """ 函数功能:从MySQL获取数据,在所有'workmate_with'关系中设置代价属性'cost',其值为(type_int+1)/(5*(1+ln(n))),其中n为同校年数。 type_int同事关系类型[0,10],n:表示x是y的第n层上级,0:表示x与y是同一级别。 :return: True """ print(time_now(1), f":************************ Workmate ************************") select_sql = "SELECT id_x, id_y, start_time, end_time, type_int FROM demo.workmate;" my_cur = mysql.cursor() my_cur.execute(select_sql) count = 0 for line in my_cur.fetchall(): # 对MySQL中的每条同事记录进行处理 count += 1 print(time_now(1), count, line) node1 = graph.data( f"MATCH (person:Person) WHERE person.id='{line[0]}' RETURN person") node2 = graph.data( f"MATCH (person:Person) WHERE person.id='{line[1]}' RETURN person") if node1 and node2: node1 = node1[0]['person'] node2 = node2[0]['person'] overlap = int(line[3][0:4]) - int(line[2][0:4]) years = 1 if overlap <= 1 else overlap # 同校年数 cost = (float(line[4]) + 1) / (5 * (1 + math.log(years, math.e)) ) # 设置代价属性'cost' create_relationship(node1, node2, 'workmate_with', 'cost', cost) # 创建同事关系 return True
def init_countrymen_cost(): """ 函数功能:连接在同一'Location'节点上的两条'is_from'关系相当于一个同乡关系,在所有'is_from'关系中设置代价属性'cost',其值为5/(3*type_int*2)。 type_int同乡关系类型(1-10):中国节点上的同乡关系类型为1,地区节点每往下一级关系类型加1,最大为10。 :return: True """ # 从'中国'节点开始 china = graph.data( "MATCH (location:Location) WHERE location.name='中国' RETURN location" )[0]['location'] type_int = 1 # 地区节点层级 cost = float(5 / type_int / 6) graph.data(f"MATCH {china}<-[r:is_from]-(:Person) SET r.cost={cost};") print( time_now(1), f":************************ {type_int} Level Location ************************" ) while type_int < 10: # 按地区层级设置代价属性'cost' type_int += 1 print( time_now(1), f":************************ {type_int} Level Location ************************" ) cost = float(5 / type_int / 6) # 设置代价属性'cost' location = graph.data( f"MATCH {china}-[:include_location*{type_int-1}]->(location:Location)" f"<-[r:is_from]-(:Person) SET r.cost={cost} RETURN location;") if not location: break return True
def init_schoolfellow_cost(): """ 函数功能:从MySQL获取数据,在所有'schoolfellow_with'关系中设置代价属性'cost',其值为5/(4*(1+ln(n))*(6-type_int)),其中n为同校年数。 type_int同学关系类型(1,2,3,4,5),1:表示同学院且同级,2:表示同学院不同级但时间有重叠,3:表示不同学院但同级,4:表示不同学院不同级但时间有重叠,5:表示同校的其他情况。 :return: True """ print(time_now(1), f":************************ Schoolfellow ************************") select_sql = "SELECT id_x, id_y, start_time, end_time, type_int FROM demo.schoolfellow;" my_cur = mysql.cursor() my_cur.execute(select_sql) count = 0 for line in my_cur.fetchall(): # 对MySQL中的每条同学记录进行处理 count += 1 print(time_now(1), count, line) node1 = graph.data( f"MATCH (person:Person) WHERE person.id='{line[0]}' RETURN person") node2 = graph.data( f"MATCH (person:Person) WHERE person.id='{line[1]}' RETURN person") if node1 and node2: node1 = node1[0]['person'] node2 = node2[0]['person'] overlap = int(line[3][0:4]) - int(line[2][0:4]) years = 1 if overlap <= 1 else overlap # 同校年数 cost = 5 / (4 * (1 + math.log(years, math.e)) * (6 - float(line[4]))) # 设置代价属性'cost' create_relationship(node1, node2, 'schoolfellow_with', 'cost', cost) # 创建同学关系 return True
def allShortestPaths(property1_value=None, property2_value=None, limit=10, n_paths=10, node1_label='Person', property1='id', node2_label='Person', property2='id', rel_type=None): """ 函数功能:从图中查找两个目的节点之间的多条最短关系路径。 返回值中relationship_group[i]表示node_group[i]与node_group[i+1]之间的关系,len(relationship_group)=len(node_group)-1, relationship_group中的‘关系方向’取值为1或0,代表'->'或'<-'。 :param property1_value: 节点1属性值 :param property2_value: 节点2属性值 :param limit: 路径的最大长度,缺省值为10 :param n_paths: 返回路径的最多条数,缺省值为10 :param node1_label: 节点1标签,缺省值为'Person' :param property1: 节点1属性,缺省值为'id' :param node2_label: 节点2标签,缺省值为'Person' :param property2: 节点2属性,缺省值为'id' :param rel_type: 允许搜索的关系类型列表,缺省值为None表示允许搜索所有关系类型 :return: 所有路径信息:多条路径节点ID列表node_group,多条路径关系类型列表relationship_group,多条路径关系方向列表direction_group。 """ last_node = None node_group = [] # 用来保存所有路径节点的列表 relationship_group = [] # 用来保存所有路径关系的列表 direction_group = [] # 用来保存所有路径关系方向的列表 node1 = graph.find_one(label=node1_label, property_key=property1, property_value=property1_value) # 查找节点1 node2 = graph.find_one(label=node2_label, property_key=property2, property_value=property2_value) # 查找节点2 get_data = graph.data( f"MATCH path=allShortestPaths({node1}-[{rel_type} *..{limit}]-{node2}) RETURN path;" ) # 查找路径 i = 0 for results in get_data: # 拆解路径信息 i += 1 print(time_now(1), i, list(walk(results['path']))) nodes, relationships, directions = [], [], [] for result in walk(results['path']): if type(result) is types.Node: last_node = result nodes.append(result['id']) # 保存当前路径的一个节点 if type(result) is types.Relationship: relationships.append(result.type()) # 保存当前路径的一个关系名称及方向 directions.append(1 if result.start_node() == last_node else 0) node_group.append(nodes) # 保存当前路径的所有节点 relationship_group.append(relationships) # 保存当前路径的所有关系名称 direction_group.append(directions) # 保存当前路径的所有关系方向 return node_group[0:n_paths], relationship_group[ 0:n_paths], direction_group[0:n_paths]
def select_schoolfellow_multi(person_id=None, school_id=None): """ 函数功能:通过目标人物ID和目标学校ID,在图中查找该人物在该学校的多种校友关系。 返回值resume_pair_int中,校友关系类型(1,2,3,4,5),1:表示同学院且同级,2:表示同学院不同级但时间有重叠,3:表示不同学院但同级,4:表示不同学院不同级但时间有重叠,5:表示同校的其他情况。 :param person_id: 目的人物ID :param school_id: 目的学校ID :return: 记录相关工作经历信息的列表 education_pair_int[目标人物ID,相关人物ID,目标教育经历ID,相关教育经历ID,重叠开始时间, 重叠结束时间,关系类型] """ # 目标人物节点 person = graph.find_one(label='Person', property_key='id', property_value=person_id) school = graph.find_one(label='School', property_key='id', property_value=school_id) academies = graph.data( f"MATCH {person}-[:study_at]->(academy:Academy)<--{school} RETURN academy;" ) if not person or not school: #如果没有目标人物节点或目标学校节点,则查找失败 return None education_pair_int = [] #初始化相关教育经历信息列表 if academies: # 如果目标人物在学院节点有过教育经历 for academy in academies: academy = academy['academy'] selece_education = graph.data( f"MATCH {person}-[r:study_at]->{academy} RETURN r;")[0]['r'] if not selece_education or selece_education['start_time'][ 0] == '0': # 如果没有这条教育经历或者教育经历开始时间年份为‘0’,则跳过该学院 continue selece_educationid = selece_education['study_id'] # 保存这条教育经历ID start_time = selece_education['start_time'] # 保存这条教育经历开始时间 # 保存这条教育经历结束时间,如果结束时间年份为‘0’,表示至今,则按格式更改为当前时间 end_time = selece_education['end_time'] if selece_education[ 'end_time'][0] != '0' else time_now(0) # 当前日期 # 查找在当前学院学习过的所有人物 resume_group = graph.data( f"MATCH {person}-[:study_at]->{academy}<-[r:study_at]-(person:Person) RETURN person, r;" ) for resume in resume_group: # 匹配教育经历时间 if person_id == resume['person']['id'] or resume['r'][ 'start_time'][ 0] == '0': # 若ID重复或开始时间年份为‘0’(数据不完整),则忽略该条记录 continue if resume['r']['end_time'][ 0] == '0': # 结束时间年份为‘0’的表示至今,按格式更改为当前时间 resume['r']['end_time'] = time_now(0) # 当前日期 # 匹配时间段重叠情况 overlap = period_cmp(start_time, end_time, resume['r']['start_time'], resume['r']['end_time']) if overlap: if start_time[0:4] == resume['r']['start_time'][ 0:4]: # 添加关系类型1:同学院且同级 education_pair_int.append([ person_id, resume['person']['id'], selece_educationid, resume['r']['study_id'], overlap[0], overlap[1], 1 ]) else: # 添加关系类型2:同学院不同级但时间有重叠 education_pair_int.append([ person_id, resume['person']['id'], selece_educationid, resume['r']['study_id'], overlap[0], overlap[1], 2 ]) else: # 添加关系类型5:同校的其他情况 education_pair_int.append([ person_id, resume['person']['id'], selece_educationid, resume['r']['study_id'], overlap[0], overlap[1], 5 ]) # 查找在当前学校的其他学院学习过的所有人物 resume_group = graph.data(f"MATCH {academy}<-[:include_academy]-(:School)-[:include_academy]->(:Academy)<-[r:study_at]-(person:Person) RETURN person, r;") \ + graph.data(f"MATCH {academy}<-[:include_academy]-(:School)<-[r:study_at]-(person:Person) RETURN person, r;") for resume in resume_group: # 匹配教育经历时间 if person_id == resume['person']['id'] or resume['r'][ 'start_time'][ 0] == '0': # 若ID重复或开始时间年份为‘0’(数据不完整),则忽略该条记录 continue if resume['r']['end_time'][ 0] == '0': # 结束时间年份为‘0’的表示至今,按格式更改为当前时间 resume['r']['end_time'] = time_now(0) # 当前日期 # 匹配时间段重叠情况 overlap = period_cmp(start_time, end_time, resume['r']['start_time'], resume['r']['end_time']) if overlap: if start_time[0:4] == resume['r']['start_time'][ 0:4]: # 添加关系类型3:不同学院但同级 education_pair_int.append([ person_id, resume['person']['id'], selece_educationid, resume['r']['study_id'], overlap[0], overlap[1], 3 ]) else: # 添加关系类型4:不同学院不同级但时间有重叠 education_pair_int.append([ person_id, resume['person']['id'], selece_educationid, resume['r']['study_id'], overlap[0], overlap[1], 4 ]) else: # 添加关系类型5:同校的其他情况 education_pair_int.append([ person_id, resume['person']['id'], selece_educationid, resume['r']['study_id'], overlap[0], overlap[1], 5 ]) # 如果目标人物在学校节点有过教育经历 selece_education = graph.data( f"MATCH {person}-[r:study_at]->{school} RETURN r;") if selece_education and not selece_education['start_time'][ 0] == '0': # 如果存在这条教育经历并且教育作经历开始时间年份不为‘0’ selece_education = selece_education[0]['r'] selece_educationid = selece_education['study_id'] # 保存这条教育经历ID start_time = selece_education['start_time'] # 保存这条教育经历开始时间 # 保存这条教育经历结束时间,如果结束时间年份为‘0’,表示至今,则按格式更改为当前时间 end_time = selece_education['end_time'] if selece_education[ 'end_time'][0] != '0' else time_now(0) # 当前日期 # 查找在当前学校的其他学院学习过的所有人物 resume_group = graph.data(f"MATCH {school}-[:include_academy]->(:Academy)<-[r:study_at]-(person:Person) RETURN person, r;") \ + graph.data(f"MATCH {person}-[:study_at]->{school}<-[r:study_at]-(person:Person) RETURN person, r;") for resume in resume_group: # 匹配教育经历时间 if person_id == resume['person']['id'] or resume['r'][ 'start_time'][0] == '0': # 若ID重复或开始时间年份为‘0’(数据不完整),则忽略该条记录 continue if resume['r']['end_time'][0] == '0': # 结束时间年份为‘0’的表示至今,按格式更改为当前时间 resume['r']['end_time'] = time_now(0) # 当前日期 # 匹配时间段重叠情况 overlap = period_cmp(start_time, end_time, resume['r']['start_time'], resume['r']['end_time']) if overlap: if start_time[0:4] == resume['r']['end_time'][ 0:4]: # 添加关系类型3:不同学院但同级 education_pair_int.append([ person_id, resume['person']['id'], selece_educationid, resume['r']['study_id'], overlap[0], overlap[1], 3 ]) else: # 添加关系类型4:不同学院不同级但时间有重叠 education_pair_int.append([ person_id, resume['person']['id'], selece_educationid, resume['r']['study_id'], overlap[0], overlap[1], 4 ]) else: # 添加关系类型5:同校的其他情况 education_pair_int.append([ person_id, resume['person']['id'], selece_educationid, resume['r']['study_id'], overlap[0], overlap[1], 5 ]) return education_pair_int # 返回相关教育经历信息列表
def dijkstraWithDefaultWeight(person1_id=None, person2_id=None, rel_type_int=[1, 2, 3, 4, 5, 6, 7], default_weight=10): """ 函数功能:从图中查找两个人物节点之间的最小代价关系路径。 关系类型[0,7],0:其他关系类型,1:亲属关系,2:联系人关系,3:同事关系,4:同学关系,5:同乡关系,6:同行人关系,7:关联关系。 返回的字符串中每个节点用‘,’隔开,若有多条路径则每条路径用‘;’隔开。 :param person1_id: 节点1属性值 :param person2_id: 节点2属性值 :param rel_type_int: 允许搜索的关系类型整数列表,为None时表示允许搜索所有关系类型,缺省值为人物间的7种关系类型 :param default_weight: 关系代价值缺省值为10 :return: 查找失败时返回False,查找成功时返回[节点信息字符串paths_nodes_id,关系类型信息字符串paths_relationships,关系方向类型信息字符串paths_directions,路径权值weight_group]。 """ if rel_type_int: rel_define = ':' for rel in rel_type_int: if rel == 1: rel_define += 'kinsfolk_with|' if rel == 2: rel_define += 'contact_with|' if rel == 3: rel_define += 'workmate_with|' if rel == 4: rel_define += 'schoolfellow_with|' if rel == 5: rel_define += 'is_from|' if rel == 6: rel_define += 'walk_with|' if rel == 7: rel_define += 'correlate_with|' else: rel_define = '' rel_define = rel_define[0:-1] last_node = None node_group = [] # 用来保存所有路径节点的列表 relationship_group = [] # 用来保存所有路径关系的列表 direction_group = [] # 用来保存所有路径关系方向的列表 weight_group = [] # 用来保存所有路径代价值的列表 get_data = graph.data( f"MATCH (person1:Person), (person2:Person) WHERE person1.id='{person1_id}' and " f"person2.id='{person2_id}' CALL apoc.algo.dijkstraWithDefaultWeight(person1,person2," f"'{rel_define}','cost',{default_weight}) YIELD path,weight RETURN path,weight;" ) # 查找路径 if not get_data: # 如果路径数据为空则查找失败 return False i = 0 for results in get_data: # 拆解路径信息 i += 1 print(time_now(1), i, list(walk(results['path']))) nodes, relationships, directions = [], [], [] for resule in walk(results['path']): if type(resule) is types.Node: last_node = resule nodes.append(resule['id']) # 保存当前路径的一个节点 if type(resule) is types.Relationship: relationships.append(resule.type()) # 保存当前路径的一个关系名称及方向 directions.append(1 if resule.start_node() == last_node else 0) node_group.append(nodes) # 保存当前路径的所有节点 relationship_group.append(relationships) # 保存当前路径的所有关系名称 direction_group.append(directions) # 保存当前路径的所有关系方向 weight_group.append(results['weight']) paths_nodes_id = '' # 初始化节点信息串 paths_relationships = '' # 初始化关系类型信息串 paths_directions = '' # 初始化关系方向类型信息串 for i in range(len(direction_group)): paths_nodes_id += node_group[i][0] key = trigger = 0 # 触发器 for j in range(len(direction_group[i])): type_rel = '0' if relationship_group[i][j] == 'kinsfolk_with': type_rel = '1' if relationship_group[i][j] == 'contact_with': type_rel = '2' if relationship_group[i][j] == 'workmate_with': type_rel = '3' if relationship_group[i][j] == 'schoolfellow_with': type_rel = '4' if relationship_group[i][j] == 'is_from': type_rel = '5' trigger += 1 if relationship_group[i][j] == 'walk_with': type_rel = '6' if relationship_group[i][j] == 'correlate_with': type_rel = '7' if trigger % 2 == 1: continue paths_nodes_id += ',' + node_group[i][j + 1] if key == 0: paths_relationships += type_rel paths_directions += str(direction_group[i][j]) key = 1 else: paths_relationships += ',' + type_rel paths_directions += ',' + str(direction_group[i][j]) if i < len(direction_group) - 1: paths_nodes_id += ';' paths_relationships += ';' paths_directions += ';' # print(paths_nodes_id) # print(paths_relationships) # print(paths_directions) return paths_nodes_id, paths_relationships, paths_directions, weight_group
def searchAndSave_allShortestPaths(person1_id=None, person2_id=None, rel_type_int=[1, 2, 3, 4, 5, 6, 7], limit=10, n_paths=10): """ 函数功能:从图中查找两个人物节点之间的多条最短关系路径,并将结果保存在MySQL数据库中。 关系类型[0,7],0:其他关系类型,1:亲属关系,2:联系人关系,3:同事关系,4:同学关系,5:同乡关系,6:同行人关系,7:关联关系。 保存的字符串中每个节点用‘,’隔开,若有多条路径则每条路径用‘;’隔开。 :param person1_id: 路径起始节点ID :param person2_id: 路径终止节点ID :param rel_type_int: 允许搜索的关系类型整数列表,为None时表示允许搜索所有关系类型,缺省值为人物间的7种关系类型 :param limit: 路径的最大长度,缺省值为10 :param n_paths: 返回路径的最多条数,缺省值为10 :return: 查找失败时返回False,查找成功时返回[节点信息字符串paths_nodes_id,关系类型信息字符串paths_relationships,关系方向类型信息字符串paths_directions]。 """ my_cur = mysql.cursor() # 获取关系型数据库游标 # MySQL查询语句 select_sql = f"SELECT * FROM demo.paths WHERE start_node_id={person1_id} AND end_node_id={person2_id} " \ f"OR start_node_id={person2_id} AND end_node_id={person1_id};" # MuSQL更新语句 update_sql = "UPDATE demo.paths SET paths_nodes_id='{0}', paths_relationships='{1}', paths_directions='{2}' " \ "WHERE start_node_id={3} AND end_node_id={4} OR start_node_id={4} AND end_node_id={3};" # MySQL插入语句,保存路径 insert_sql = "INSERT INTO demo.paths(start_node_id, end_node_id, paths_nodes_id, paths_relationships, paths_directions) " \ "VALUES ('{0}', '{1}', '{2}', '{3}', '{4}');" if rel_type_int: rel_define = ':' for rel in rel_type_int: if rel == 1: rel_define += 'kinsfolk_with|' if rel == 2: rel_define += 'contact_with|' if rel == 3: rel_define += 'workmate_with|' if rel == 4: rel_define += 'schoolfellow_with|' if rel == 5: rel_define += 'is_from|' if rel == 6: rel_define += 'walk_with|' if rel == 7: rel_define += 'correlate_with|' else: rel_define = '' rel_define = rel_define[0:-1] paths = allShortestPaths(person1_id, person2_id, limit, n_paths, 'Person', 'id', 'Person', 'id', rel_define) if not paths[2]: # 如果路径关系方向类型列表为空则查找失败 return False paths_nodes_id = '' # 初始化节点信息串 paths_relationships = '' # 初始化关系类型信息串 paths_directions = '' # 初始化关系方向类型信息串 for i in range(len(paths[2])): paths_nodes_id += paths[0][i][0] key = trigger = 0 # 触发器 for j in range(len(paths[2][i])): type_rel = '0' if paths[1][i][j] == 'kinsfolk_with': type_rel = '1' if paths[1][i][j] == 'contact_with': type_rel = '2' if paths[1][i][j] == 'workmate_with': type_rel = '3' if paths[1][i][j] == 'schoolfellow_with': type_rel = '4' if paths[1][i][j] == 'is_from': type_rel = '5' trigger += 1 if paths[1][i][j] == 'walk_with': type_rel = '6' if paths[1][i][j] == 'correlate_with': type_rel = '7' if trigger % 2 == 1: continue paths_nodes_id += ',' + paths[0][i][j + 1] if key == 0: paths_relationships += type_rel paths_directions += str(paths[2][i][j]) key = 1 else: paths_relationships += ',' + type_rel paths_directions += ',' + str(paths[2][i][j]) if i < len(paths[2]) - 1: paths_nodes_id += ';' paths_relationships += ';' paths_directions += ';' if my_cur.execute(select_sql): try: my_cur.execute( update_sql.format(paths_nodes_id, paths_relationships, paths_directions, person1_id, person2_id)) mysql.commit() print(time_now(1), ':Paths-MySQL Update Successful:', person1_id, person2_id) except: mysql.rollback() # 插入失败,执行回滚操作 print(time_now(1), ':Paths-MySQL Update Error:', person1_id, person2_id) else: try: my_cur.execute( insert_sql.format(person1_id, person2_id, paths_nodes_id, paths_relationships, paths_directions)) mysql.commit() print(time_now(1), ':Paths-MySQL Insert Successful:', person1_id, person2_id) except: mysql.rollback() # 插入失败,执行回滚操作 print(time_now(1), ':Paths-MySQL Insert Error:', person1_id, person2_id) my_cur.close() return paths_nodes_id, paths_relationships, paths_directions
def select_workmate_multi(person_id=None, position_id=None, max_level=1): """ 函数功能:通过目标人物ID和目标职位ID,在图中查找该人物的复杂同事关系。 返回值resume_pair_int中,同事关系类型(0-10),n:表示x是y的第n层上级,0:表示x与y是同一级别。双向关系只保存一条记录。 :param person_id: 目的人物ID :param position_id: 目的职位ID :param max_level: 最大允许查找上下级的层数,缺省值为1 :return: 记录相关工作经历信息的列表 resume_pair_int[目标人物ID,相关人物ID,目标工作经历ID,相关工作经历ID,重叠开始时间, 重叠结束时间,关系类型] """ # 目标人物节点 resume = graph.find_one(label='Person', property_key='id', property_value=person_id) # 目标职位节点 position = graph.find_one(label='Position', property_key='id', property_value=position_id) # 目标人物在目标职位的工作经历 selece_resume = graph.match_one(start_node=resume, end_node=position, rel_type='work_at', bidirectional=True) if not selece_resume or selece_resume['start_time'][ 0] == '0': # 如果没有这条工作经历或者工作经历开始时间年份为‘0’,则查找失败 return False selece_resumeid = selece_resume['work_id'] # 保存这条工作经历ID start_time = selece_resume['start_time'] # 保存这条工作经历开始时间 # 保存这条工作经历结束时间,如果结束时间年份为‘0’,表示至今,则按格式更改为当前时间 end_time = selece_resume[ 'end_time'] if selece_resume['end_time'][0] != '0' else time_now( 0) # 当前日期 resume_pair_int = [] # 初始化:相关工作经历信息列表 # 查找在当前职位工作的所有人物 resume_group = graph.data( f"MATCH {resume}-[:work_at]-{position}-[r:work_at]-(person:Person) RETURN person, r;" ) for resume in resume_group: # 匹配教育经历时间 if person_id == resume['person']['id'] or resume['r']['start_time'][ 0] == '0': # 若ID重复或开始时间年份为‘0’(数据不完整),则忽略该条记录 continue if resume['r']['end_time'][0] == '0': # 结束时间年份为‘0’的表示至今,按格式更改为当前时间 resume['r']['end_time'] = time_now(0) # 当前日期 # 匹配时间段重叠情况 overlap = period_cmp(start_time, end_time, resume['r']['start_time'], resume['r']['end_time']) if overlap: # 添加相关工作经历信息 resume_pair_int.append([ person_id, resume['person']['id'], selece_resumeid, resume['r']['work_id'], overlap[0], overlap[1], 0 ]) rel_level = 0 # 控制查找层级 while rel_level < max_level: # 按层次依次查找下级职位节点的工作经历 rel_level += 1 persons_down = graph.data( f"MATCH (person:Person)-[r:work_at]->(position:Position)<-" f"[:include_position*{rel_level}]-{position} RETURN position, r, person;" ) if not persons_down[0]: # 当没有更低级职位时退出 break for resume in persons_down: for i in range(len(persons_down) - 1): if selece_resumeid == resume['r']['work_id'] or resume['r'][ 'start_time'][ 0] == '0': # 若ID重复或开始时间年份为‘0’(数据不完整),则忽略该条记录 continue if resume['r']['end_time'][ 0] == '0': # 结束时间年份为‘0’的表示至今,按格式更改为当前时间 resume['r']['end_time'] = time_now(0) # 当前日期 # 匹配时间段重叠情况 overlap = period_cmp(start_time, end_time, resume['r']['start_time'], resume['r']['end_time']) if overlap: # 添加相关工作经历信息 resume_pair_int.append([ person_id, resume['person']['id'], selece_resumeid, resume['r']['work_id'], overlap[0], overlap[1], rel_level ]) rel_level = 0 # 控制查找层级 while rel_level < max_level: # 按层次依次查找上级职位节点的工作经历 rel_level += 1 persons_up = graph.data( f"MATCH (person:Person)-[r:work_at]->(position:Position)-" f"[:include_position*{rel_level}]->{position} RETURN position, r, person;" ) if not persons_up[0]: # 当没有更高级职位时退出 break for resume in persons_up: for i in range(len(persons_up) - 1): if selece_resumeid == resume['r']['work_id'] or resume['r'][ 'start_time'][ 0] == '0': # 若ID重复或开始时间年份为‘0’(数据不完整),则忽略该条记录 continue if resume['r']['end_time'][ 0] == '0': # 结束时间年份为‘0’的表示至今,按格式更改为当前时间 resume['r']['end_time'] = time_now(0) # 当前日期 # 匹配时间段重叠情况 overlap = period_cmp(start_time, end_time, resume['r']['start_time'], resume['r']['end_time']) if overlap: # 添加相关工作经历信息 resume_pair_int.append([ resume['person']['id'], person_id, resume['r']['work_id'], selece_resumeid, overlap[0], overlap[1], rel_level ]) return resume_pair_int # 返回相关工作经历信息列表