''' 根据标签和类型来查找对应的符合条件的人、企业、职位 ''' from flask import current_app from configs.connections import connect_graph def similar_seeker(label): try: query = """ WITH $Label AS target_labels UNWIND target_labels AS target_label MATCH (jl:seekerLabel) WITH target_label, jl, apoc.text.distance(target_label, jl.name) AS distance WITH target_label, jl, 1.0 / (1.0 + distance) AS similarity WHERE similarity > 0.5 WITH target_label, jl, similarity MATCH (j:seeker)-[:connection]->(jl) WITH j, similarity ORDER BY similarity DESC WITH DISTINCT j, similarity RETURN j.uniqueId AS ids_list ORDER BY similarity DESC """ result = connect_graph.run(query, Label=label) if result: return [record['ids_list'] for record in result] # 提取所有 uniqueId else: return [] except Exception as e: current_app.logger.error(f'similar_seeker error: {e}') return str(e) def draw_graph(): try: # 先判断图谱中是否存在投影图,不存在直接drop会报错 create_query = """CALL gds.graph.exists('jobGraph') YIELD exists RETURN CASE exists WHEN true THEN 1 ELSE 0 END as graphExists""" result = connect_graph.run(create_query).evaluate() if result == 0: create_query = """ // 创建图投影 CALL gds.graph.project( 'jobGraph', ['job', 'jobLabel'], { r: { type: 'connection', orientation: 'UNDIRECTED' } } ) """ connect_graph.run(create_query) except Exception as e: current_app.logger.error(f'draw_graph error: {e}') return str(e) # 职位详情推荐类似的职位 def similar_job(uniqueid,start,end): try: if uniqueid: query = f""" // 计算 Jaccard 相似度 CALL gds.nodeSimilarity.stream('jobGraph', {{ similarityCutoff: 0.0, topK: 10 }}) YIELD node1, node2, similarity WITH gds.util.asNode(node1) AS node1, gds.util.asNode(node2) AS node2, similarity WHERE node1.uniqueId = $Id AND node2.uniqueId <> $Id WITH node2.uniqueId AS job_id, node2.name AS job_name, similarity ORDER BY similarity DESC // 返回最相似的 job 节点 //with count(job_id) as total,collect(job_id) as job_ids with collect(job_id) as job_ids return job_ids[{start}..{end}] as jobIds """ result = connect_graph.run(query, Id=uniqueid).evaluate() if result: return result else: return default_job() else: return default_job() except Exception as e: current_app.logger.error(f'similar_job error: {e}') return default_job() # 职位详情推荐类似的职位 def home_job(uniqueid,start,end): try: if uniqueid: query = f""" // 1. 查找 seeker 节点 MATCH (s:seeker {{uniqueId: $Id}}) // 2. 获取 seekerLabel 节点 WITH s MATCH (s)-[:connection]->(sl:seekerLabel) // 3. 获取 jobLabel 节点 WITH s, sl MATCH (jl:jobLabel) // 4. 计算 seekerLabel 和 jobLabel 之间的 Levenshtein 距离 WITH s, sl, jl, apoc.text.levenshteinDistance(sl.name, jl.name) AS distance // 5. 找到距离最小的 jobLabel WITH s, sl, jl, distance ORDER BY distance ASC WITH s, sl, collect({{jl: jl, distance: distance}}) AS jobLabels WITH s, sl, jobLabels, min(jobLabels[0].distance) AS min_distance UNWIND jobLabels AS jl_with_distance WITH s, sl, jl_with_distance.jl AS jl, jl_with_distance.distance AS distance, min_distance WHERE distance <= min_distance // 6. 获取 job 节点 WITH s, sl, jl MATCH (j:job)-[:connection]->(jl) // 7. 返回 job 的 id 和总数 WITH s, j.uniqueId AS jobId //WITH count(DISTINCT jobId) AS total, collect(DISTINCT jobId) AS jobIds with collect(DISTINCT jobId) AS jobIds RETURN jobIds[{start}..{end}] AS jobIds """ result = connect_graph.run(query,Id = uniqueid).evaluate() if result: return result else: return default_job() else: return default_job() except Exception as e: current_app.logger.error(f'home_job error: {e}') return default_job() def default_job(): try: query = """ MATCH (n:job)-[r:connection]-(m:jobLabel) WITH n, COUNT(m) AS num_connections ORDER BY num_connections DESC LIMIT 10 RETURN collect(n.uniqueId) AS job_ids """ result = connect_graph.run(query).evaluate() return result except Exception as e: current_app.logger.error(f'default_job error: {e}') return [] def draw_person_graph(): try: # 先判断图谱中是否存在投影图,不存在直接drop会报错 create_query = """CALL gds.graph.exists('personGraph') YIELD exists RETURN CASE exists WHEN true THEN 1 ELSE 0 END as graphExists""" result = connect_graph.run(create_query).evaluate() if result == 0: create_query = """ // 创建图投影 CALL gds.graph.project( 'personGraph', ['seeker', 'seekerLabel'], { r: { type: 'connection', orientation: 'UNDIRECTED' } } ) """ connect_graph.run(create_query) except Exception as e: current_app.logger.error(f'draw_graph error: {e}') return str(e) def home_person(uniqueid,start,end): try: if uniqueid: query = f""" // 1. 查找 job 节点 MATCH (j:job {{uniqueId: $Id}}) // 2. 获取 jobLabel 节点 WITH j MATCH (j)-[:connection]->(jl:jobLabel) // 3. 获取 seekerLabel 节点 WITH j, jl MATCH (sl:seekerLabel) // 4. 计算jobLabel 和 seekerLabel 之间的 Levenshtein 距离 WITH j, jl, sl, apoc.text.levenshteinDistance( jl.name,sl.name) AS distance // 5. 找到距离最小的 seekerLabel WITH j, jl, sl, distance ORDER BY distance ASC WITH j, jl, collect({{sl: sl, distance: distance}}) AS seekerLabels WITH j, jl,seekerLabels, min(seekerLabels[0].distance) AS min_distance UNWIND seekerLabels AS sl_with_distance WITH j, jl, sl_with_distance.sl AS sl, sl_with_distance.distance AS distance, min_distance WHERE distance <= min_distance // 6. 获取 seeker 节点 WITH j, jl, sl MATCH (s:seeker)-[:connection]->(sl) // 7. 返回 seeker 的 id 和总数 WITH j, s.uniqueId AS seekerId //WITH count(DISTINCT seekerId) AS total, collect(DISTINCT seekerId) AS seekerIds with collect(DISTINCT seekerId) AS seekerIds RETURN seekerIds[{start}..{end}] AS seekerIds """ result = connect_graph.run(query,Id = uniqueid).evaluate() if result: return result else: return default_person() else: return default_person() except Exception as e: current_app.logger.error(f'home_job error: {e}') return default_person() def default_person(): try: query = """ MATCH (n:seeker)-[r:connection]-(m:seekerLabel) WITH n, COUNT(m) AS num_connections ORDER BY num_connections DESC LIMIT 10 RETURN collect(n.uniqueId) AS seeker_ids """ result = connect_graph.run(query).evaluate() return result except Exception as e: current_app.logger.error(f'default_person error: {e}') return []