utils.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
  1. package main
  2. import (
  3. "fmt"
  4. nebula "github.com/vesoft-inc/nebula-go/v3"
  5. "log"
  6. "strings"
  7. )
  8. // 表示企业关系结果
  9. //type AllRelationResult struct {
  10. // RelatedCompanies []string // 有关系的企业列表
  11. // Paths []string // 对应的路径
  12. //}
  13. //
  14. //// 批量获取企业的 VID
  15. //func getVidsByName(session *nebula.Session, names []string) (map[string]string, error) {
  16. // if len(names) == 0 {
  17. // return nil, nil
  18. // }
  19. // conditions := ""
  20. // for i, name := range names {
  21. // if i > 0 {
  22. // conditions += " OR "
  23. // }
  24. // conditions += fmt.Sprintf("Legal.name == \"%s\"", name)
  25. // }
  26. // query := fmt.Sprintf(`
  27. //USE %s;
  28. //LOOKUP ON Legal WHERE %s YIELD id(vertex) AS vid, properties(vertex).name AS name
  29. //`, Table_Space, conditions)
  30. // resp, err := session.Execute(query)
  31. // if err != nil {
  32. // return nil, err
  33. // }
  34. // nameToVid := make(map[string]string)
  35. // for _, row := range resp.GetRows() {
  36. // if len(row.Values) >= 2 {
  37. // if row.Values[0].SVal != nil && row.Values[1].SVal != nil {
  38. // nameToVid[string(row.Values[1].SVal)] = string(row.Values[0].SVal)
  39. // }
  40. // }
  41. // }
  42. // return nameToVid, nil
  43. //}
  44. //
  45. //// 获取 VID 对应的名称
  46. //func getVidName(session *nebula.Session, vid string) (string, error) {
  47. // query := fmt.Sprintf(`
  48. //USE %s;
  49. //FETCH PROP ON Legal "%s" YIELD properties(vertex).name AS name
  50. //`, Table_Space, vid)
  51. // resp, err := session.Execute(query)
  52. // if err != nil {
  53. // return "", err
  54. // }
  55. // names, err := getFirstColumnStrings(resp)
  56. // if err != nil || len(names) == 0 {
  57. // return "", fmt.Errorf("未找到 VID %s 的名称", vid)
  58. // }
  59. // return names[0], nil
  60. //}
  61. //
  62. //// 查找路径
  63. //func findPath(session *nebula.Session, fromVid, toVid string, maxStep int, pathCache map[string][]string) ([]string, error) {
  64. // key := fmt.Sprintf("%s->%s:%d", fromVid, toVid, maxStep)
  65. // if cachedPath, ok := pathCache[key]; ok {
  66. // return cachedPath, nil
  67. // }
  68. // query := fmt.Sprintf(`FIND ALL PATH FROM "%s" TO "%s" OVER Invest UPTO %d STEPS YIELD path as p`, fromVid, toVid, maxStep)
  69. // resp, err := session.Execute(query)
  70. // if err != nil {
  71. // return nil, err
  72. // }
  73. // path, err := getFirstColumnStrings(resp)
  74. // if err != nil {
  75. // return nil, err
  76. // }
  77. // pathCache[key] = path
  78. // return path, nil
  79. //}
  80. //
  81. //// 检查共同祖先
  82. //func checkCommonAncestor(session *nebula.Session, aVid, bVid string, deep int, pathCache map[string][]string) (bool, []string, string) {
  83. // key := fmt.Sprintf("%s&%s:%d", aVid, bVid, deep)
  84. // if cachedPath, ok := pathCache[key]; ok {
  85. // if len(cachedPath) > 0 {
  86. // return true, cachedPath, cachedPath[1]
  87. // }
  88. // return false, nil, ""
  89. // }
  90. // query := fmt.Sprintf(`
  91. // (
  92. // GO 1 TO %d STEPS FROM "%s" OVER Invest REVERSELY YIELD dst(edge) AS ancestor
  93. // )
  94. // INTERSECT
  95. // (
  96. // GO 1 TO %d STEPS FROM "%s" OVER Invest REVERSELY YIELD dst(edge) AS ancestor
  97. // );
  98. // `, deep, aVid, deep, bVid)
  99. // resp, err := session.Execute(query)
  100. // if err != nil {
  101. // return false, nil, ""
  102. // }
  103. // ancestors, err := getFirstColumnStrings(resp)
  104. // if err != nil || len(ancestors) == 0 {
  105. // pathCache[key] = nil
  106. // return false, nil, ""
  107. // }
  108. // pathA, _ := findPath(session, aVid, ancestors[0], deep, pathCache)
  109. // pathB, _ := findPath(session, bVid, ancestors[0], deep, pathCache)
  110. // var path []string
  111. // if len(pathB) > 1 {
  112. // path = append(pathA, pathB[1:]...)
  113. // } else {
  114. // path = append(pathA, pathB...)
  115. // }
  116. // pathCache[key] = path
  117. // return true, path, ancestors[0]
  118. //}
  119. //
  120. //// 将 VID 路径转换为名称路径
  121. //func convertVidPathToNamePath(session *nebula.Session, vidPath []string) (string, error) {
  122. // namePath := ""
  123. // for i, vid := range vidPath {
  124. // name, err := getVidName(session, vid)
  125. // if err != nil {
  126. // return "", err
  127. // }
  128. // if i > 0 {
  129. // namePath += "->"
  130. // }
  131. // namePath += name
  132. // }
  133. // return namePath, nil
  134. //}
  135. //
  136. //// 检查企业关系
  137. //func CheckLegalRelations(session *nebula.Session, names []string, deep int) (AllRelationResult, error) {
  138. // result := AllRelationResult{}
  139. // checked := make(map[string]bool)
  140. // nameToVid, err := getVidsByName(session, names)
  141. // if err != nil {
  142. // return result, err
  143. // }
  144. // pathCache := make(map[string][]string)
  145. // relatedCompaniesSet := make(map[string]bool)
  146. // var paths []string
  147. //
  148. // for i := 0; i < len(names); i++ {
  149. // for j := i + 1; j < len(names); j++ {
  150. // a, b := names[i], names[j]
  151. // vidA, okA := nameToVid[a]
  152. // vidB, okB := nameToVid[b]
  153. // if !okA || !okB {
  154. // continue
  155. // }
  156. //
  157. // key := vidA + "|" + vidB
  158. // if checked[key] {
  159. // continue
  160. // }
  161. // checked[key] = true
  162. //
  163. // // 1. a -> b
  164. // pathAB, err := findPath(session, vidA, vidB, deep, pathCache)
  165. // if err != nil {
  166. // log.Printf("查找 %s 到 %s 的路径失败: %v", a, b, err)
  167. // continue
  168. // }
  169. // if len(pathAB) > 0 {
  170. // pathStr, err := convertVidPathToNamePath(session, pathAB)
  171. // if err != nil {
  172. // log.Printf("转换 %s 到 %s 的路径失败: %v", a, b, err)
  173. // continue
  174. // }
  175. // paths = append(paths, pathStr)
  176. // for _, vid := range pathAB {
  177. // name, err := getVidName(session, vid)
  178. // if err != nil {
  179. // log.Printf("获取 VID %s 对应的名称失败: %v", vid, err)
  180. // continue
  181. // }
  182. // relatedCompaniesSet[name] = true
  183. // }
  184. // continue
  185. // }
  186. //
  187. // // 2. b -> a
  188. // pathBA, err := findPath(session, vidB, vidA, deep, pathCache)
  189. // if err != nil {
  190. // log.Printf("查找 %s 到 %s 的路径失败: %v", b, a, err)
  191. // continue
  192. // }
  193. // if len(pathBA) > 0 {
  194. // pathStr, err := convertVidPathToNamePath(session, pathBA)
  195. // if err != nil {
  196. // log.Printf("转换 %s 到 %s 的路径失败: %v", b, a, err)
  197. // continue
  198. // }
  199. // paths = append(paths, pathStr)
  200. // for _, vid := range pathBA {
  201. // name, err := getVidName(session, vid)
  202. // if err != nil {
  203. // log.Printf("获取 VID %s 对应的名称失败: %v", vid, err)
  204. // continue
  205. // }
  206. // relatedCompaniesSet[name] = true
  207. // }
  208. // continue
  209. // }
  210. //
  211. // // 3. common ancestor
  212. // common, path, _ := checkCommonAncestor(session, vidA, vidB, deep, pathCache)
  213. // if common {
  214. // pathStr, err := convertVidPathToNamePath(session, path)
  215. // if err != nil {
  216. // log.Printf("转换 %s 和 %s 到共同祖先的路径失败: %v", a, b, err)
  217. // continue
  218. // }
  219. // paths = append(paths, pathStr)
  220. // for _, vid := range path {
  221. // name, err := getVidName(session, vid)
  222. // if err != nil {
  223. // log.Printf("获取 VID %s 对应的名称失败: %v", vid, err)
  224. // continue
  225. // }
  226. // relatedCompaniesSet[name] = true
  227. // }
  228. // }
  229. // }
  230. // }
  231. //
  232. // for company := range relatedCompaniesSet {
  233. // result.RelatedCompanies = append(result.RelatedCompanies, company)
  234. // }
  235. // result.Paths = paths
  236. //
  237. // return result, nil
  238. //}
  239. //
  240. //// getFirstColumnStrings 适配 nebula-go v3 取出字符串类型列
  241. //func getFirstColumnStrings(resp *nebula.ResultSet) ([]string, error) {
  242. // if resp == nil {
  243. // return nil, fmt.Errorf("result set is nil")
  244. // }
  245. //
  246. // var values []string
  247. // for _, row := range resp.GetRows() {
  248. // if len(row.Values) == 0 {
  249. // continue
  250. // }
  251. // val := row.Values[0]
  252. // switch {
  253. // case val.SVal != nil:
  254. // values = append(values, string(val.SVal))
  255. // case val.IVal != nil:
  256. // values = append(values, fmt.Sprintf("%d", *val.IVal))
  257. // case val.BVal != nil:
  258. // values = append(values, fmt.Sprintf("%v", *val.BVal))
  259. // default:
  260. // log.Printf("未知类型值: %+v", val)
  261. // }
  262. // }
  263. // return values, nil
  264. //}
  265. func CheckLegalRelations(session *nebula.Session, names []string, deep int) ([]RelationResult, error) {
  266. results := []RelationResult{}
  267. checked := make(map[string]bool)
  268. nameToVid, err := getAllVids(session, names)
  269. if err != nil {
  270. return nil, err
  271. }
  272. vidToName := reverseMap(nameToVid)
  273. for i := 0; i < len(names); i++ {
  274. for j := i + 1; j < len(names); j++ {
  275. a, b := names[i], names[j]
  276. vidA, okA := nameToVid[a]
  277. vidB, okB := nameToVid[b]
  278. if !okA || !okB {
  279. continue
  280. }
  281. key := vidA + "|" + vidB
  282. if checked[key] {
  283. continue
  284. }
  285. checked[key] = true
  286. // 1. a -> b
  287. pathAB, err := findPath(session, vidA, vidB, deep)
  288. if err != nil {
  289. return nil, err
  290. }
  291. if len(pathAB) > 0 {
  292. readablePath := convertPathToNames(pathAB, vidToName)
  293. results = append(results, RelationResult{A: a, B: b, RelationType: "direct_or_indirect", Path: readablePath})
  294. continue
  295. }
  296. // 2. b -> a
  297. pathBA, err := findPath(session, vidB, vidA, deep)
  298. if err != nil {
  299. return nil, err
  300. }
  301. if len(pathBA) > 0 {
  302. readablePath := convertPathToNames(pathBA, vidToName)
  303. results = append(results, RelationResult{A: b, B: a, RelationType: "direct_or_indirect", Path: readablePath})
  304. continue
  305. }
  306. // 3. common ancestor
  307. common, ancestorVid, err := checkCommonAncestor(session, vidA, vidB, deep)
  308. if err != nil {
  309. return nil, err
  310. }
  311. if common {
  312. ancestorName := getAncestorName(session, ancestorVid, vidToName)
  313. aName := vidToName[vidA]
  314. bName := vidToName[vidB]
  315. if ancestorName != "" && aName != "" && bName != "" {
  316. readablePath := []string{
  317. fmt.Sprintf("%s -> %s", aName, ancestorName),
  318. fmt.Sprintf("%s -> %s", bName, ancestorName),
  319. }
  320. results = append(results, RelationResult{A: a, B: b, RelationType: "common_ancestor", Path: readablePath})
  321. }
  322. }
  323. }
  324. }
  325. return results, nil
  326. }
  327. func getAllVids(session *nebula.Session, names []string) (map[string]string, error) {
  328. nameToVid := make(map[string]string)
  329. for _, name := range names {
  330. vid, err := getVidByName(session, name)
  331. if err != nil {
  332. log.Printf("获取 %s 的 VID 失败: %v", name, err)
  333. continue
  334. }
  335. nameToVid[name] = vid
  336. }
  337. return nameToVid, nil
  338. }
  339. func checkCommonAncestor(session *nebula.Session, aVid, bVid string, deep int) (bool, string, error) {
  340. query := fmt.Sprintf(`
  341. (
  342. GO 1 TO %d STEPS FROM "%s" OVER Invest REVERSELY YIELD dst(edge) AS ancestor
  343. )
  344. INTERSECT
  345. (
  346. GO 1 TO %d STEPS FROM "%s" OVER Invest REVERSELY YIELD dst(edge) AS ancestor
  347. );
  348. `, deep, aVid, deep, bVid)
  349. resp, err := session.Execute(query)
  350. if err != nil {
  351. return false, "", err
  352. }
  353. ancestors, err := getFirstColumnStrings(resp)
  354. if err != nil || len(ancestors) == 0 {
  355. return false, "", nil
  356. }
  357. return true, ancestors[0], nil
  358. }
  359. func findPath(session *nebula.Session, fromVid, toVid string, maxStep int) ([]string, error) {
  360. query := fmt.Sprintf(`FIND ALL PATH FROM "%s" TO "%s" OVER Invest UPTO %d STEPS YIELD path as p`, fromVid, toVid, maxStep)
  361. resp, err := session.Execute(query)
  362. if err != nil {
  363. return nil, err
  364. }
  365. return getFirstColumnStrings(resp)
  366. }
  367. func getVidByName(session *nebula.Session, name string) (string, error) {
  368. query := fmt.Sprintf(`
  369. USE `+Table_Space+`;
  370. LOOKUP ON Legal WHERE Legal.name == "%s" YIELD id(vertex)`, name)
  371. resp, err := session.Execute(query)
  372. if err != nil {
  373. return "", err
  374. }
  375. values, err := getFirstColumnStrings(resp)
  376. if err != nil || len(values) == 0 {
  377. return "", fmt.Errorf("未找到公司: %s", name)
  378. }
  379. return values[0], nil
  380. }
  381. type RelationResult struct {
  382. A, B string // 公司名
  383. RelationType string // 关系类型:"direct_or_indirect", "common_ancestor"
  384. Path []string // 路径中的公司名称,以更直观的形式展示
  385. }
  386. // getFirstColumnStrings 适配 nebula-go v3 取出字符串类型列
  387. func getFirstColumnStrings(resp *nebula.ResultSet) ([]string, error) {
  388. if resp == nil {
  389. return nil, fmt.Errorf("result set is nil")
  390. }
  391. var values []string
  392. for _, row := range resp.GetRows() {
  393. if len(row.Values) == 0 {
  394. continue
  395. }
  396. val := row.Values[0]
  397. switch {
  398. case val.SVal != nil:
  399. values = append(values, string(val.SVal))
  400. case val.IVal != nil:
  401. values = append(values, fmt.Sprintf("%d", *val.IVal))
  402. case val.BVal != nil:
  403. values = append(values, fmt.Sprintf("%v", *val.BVal))
  404. case val.PVal != nil:
  405. // 处理点类型
  406. src := val.PVal.GetSrc()
  407. log.Println(src)
  408. //if src.GetId != nil {
  409. // values = append(values, string(*src.SVal))
  410. //} else if src.IVal != nil {
  411. // values = append(values, fmt.Sprintf("%d", *src.IVal))
  412. //} else {
  413. // log.Printf("未知的点源 ID 类型: %+v", src)
  414. //}
  415. default:
  416. log.Printf("未知类型值: %+v", val)
  417. }
  418. }
  419. return values, nil
  420. }
  421. func reverseMap(m map[string]string) map[string]string {
  422. result := make(map[string]string)
  423. for k, v := range m {
  424. result[v] = k
  425. }
  426. return result
  427. }
  428. func convertPathToNames(path []string, vidToName map[string]string) []string {
  429. readablePath := make([]string, 0, len(path))
  430. for i := 0; i < len(path)-1; i++ {
  431. fromName, okFrom := vidToName[path[i]]
  432. toName, okTo := vidToName[path[i+1]]
  433. if okFrom && okTo && fromName != toName {
  434. readablePath = append(readablePath, fmt.Sprintf("%s -> %s", fromName, toName))
  435. }
  436. }
  437. return readablePath
  438. }
  439. func getAncestorName(session *nebula.Session, ancestorVid string, vidToName map[string]string) string {
  440. if name, ok := vidToName[ancestorVid]; ok {
  441. return name
  442. }
  443. query := fmt.Sprintf(`
  444. USE `+Table_Space+`;
  445. FETCH PROP ON Legal "%s" YIELD Legal.name;
  446. `, ancestorVid)
  447. resp, err := session.Execute(query)
  448. if err != nil {
  449. log.Printf("获取祖先公司名称失败: %v", err)
  450. return ""
  451. }
  452. names, err := getFirstColumnStrings(resp)
  453. if err != nil || len(names) == 0 {
  454. return ""
  455. }
  456. return names[0]
  457. }
  458. //2222222222222222//
  459. // CheckLegalRelationships 检查给定企业之间是否存在关系
  460. func CheckLegalRelationships(session *nebula.Session, names []string, deep, stype int) (bool, string, error) {
  461. if len(names) < 2 {
  462. return false, "", fmt.Errorf("企业数量不足,至少需要两个")
  463. }
  464. found := false // 是否找到任意关系
  465. for i := 0; i < len(names); i++ {
  466. start := names[i]
  467. // 构造剩下的企业列表作为目标
  468. targets := []string{}
  469. for j := 0; j < len(names); j++ {
  470. if i != j {
  471. targets = append(targets, fmt.Sprintf(`"%s"`, names[j]))
  472. }
  473. }
  474. targetList := strings.Join(targets, ", ")
  475. // 构造 nGQL 查询
  476. query := fmt.Sprintf(`
  477. USE %s;
  478. MATCH p=(a:Legal{name:"%s"})-[*1..%d]-(b:Legal)
  479. WHERE b.Legal.name IN [%s]
  480. RETURN p LIMIT 1
  481. `, Table_Space, start, deep, targetList)
  482. // 执行查询
  483. resp, err := session.Execute(query)
  484. if err != nil {
  485. return false, "", fmt.Errorf("查询失败: %w", err)
  486. }
  487. if !resp.IsSucceed() {
  488. return false, "", fmt.Errorf("查询执行失败: %s", resp.GetErrorMsg())
  489. }
  490. if resp.GetRowSize() > 0 {
  491. found = true
  492. // stype == 0:只要找到一个关系就返回
  493. if stype == 0 {
  494. return true, "企业之间存在关系", nil
  495. }
  496. }
  497. }
  498. // 所有组合查完,判断是否找到关系
  499. if found {
  500. return true, "企业之间存在关系", nil
  501. }
  502. return false, "企业之间无关联", nil
  503. }
  504. func CheckLegalRelationships2(session *nebula.Session, names []string, deep, stype int) (bool, []string, error) {
  505. if len(names) < 2 {
  506. return false, nil, fmt.Errorf("企业数量不足,至少需要两个")
  507. }
  508. var allPaths []string
  509. for i := 0; i < len(names); i++ {
  510. start := names[i]
  511. // 构造剩下的企业列表作为目标
  512. targets := []string{}
  513. for j := 0; j < len(names); j++ {
  514. if i != j {
  515. targets = append(targets, fmt.Sprintf(`"%s"`, names[j]))
  516. }
  517. }
  518. targetList := strings.Join(targets, ", ")
  519. // 构造查询语句
  520. query := fmt.Sprintf(`
  521. USE %s;
  522. MATCH p=(a:Legal{name:"%s"})-[*1..%d]-(b:Legal)
  523. WHERE b.Legal.name IN [%s]
  524. RETURN p LIMIT 1
  525. `, Table_Space, start, deep, targetList)
  526. resp, err := session.Execute(query)
  527. if err != nil {
  528. return false, nil, fmt.Errorf("查询失败: %w", err)
  529. }
  530. if !resp.IsSucceed() {
  531. return false, nil, fmt.Errorf("查询执行失败: %s", resp.GetErrorMsg())
  532. }
  533. if resp.GetRowSize() > 0 {
  534. // 解析路径
  535. for _, row := range resp.GetRows() {
  536. if len(row.Values) == 0 {
  537. continue
  538. }
  539. val := row.Values[0]
  540. if !val.IsSetPVal() {
  541. continue
  542. }
  543. path := val.GetPVal()
  544. var pathNames []string
  545. // 起点
  546. srcVertex := path.Src
  547. if srcVertex != nil && srcVertex.Vid != nil && srcVertex.Vid.IsSetSVal() {
  548. vid := string(srcVertex.Vid.GetSVal())
  549. lea, err := getLegalByVid(session, vid)
  550. if err != nil {
  551. log.Println("getVidByName err", err, vid)
  552. }
  553. pathNames = append(pathNames, lea.Name)
  554. }
  555. // 步骤中目的点
  556. for _, step := range path.Steps {
  557. dstVertex := step.Dst
  558. if dstVertex != nil && dstVertex.Vid != nil && dstVertex.Vid.IsSetSVal() {
  559. vid := string(dstVertex.Vid.GetSVal())
  560. lea, err := getLegalByVid(session, vid)
  561. if err != nil {
  562. log.Println("getVidByName err", err, vid)
  563. }
  564. pathNames = append(pathNames, lea.Name)
  565. }
  566. }
  567. if len(pathNames) >= 2 {
  568. dd := strings.Join(pathNames, " -> ")
  569. allPaths = append(allPaths, dd)
  570. if stype == 0 {
  571. return true, allPaths, nil
  572. }
  573. }
  574. }
  575. }
  576. }
  577. if len(allPaths) > 0 {
  578. return true, allPaths, nil
  579. }
  580. return false, nil, nil
  581. }
  582. // getLegalByVid 根据vid 获取顶点
  583. func getLegalByVid(session *nebula.Session, vid string) (*Legal, error) {
  584. query := fmt.Sprintf(`
  585. USE %s;
  586. FETCH PROP ON Legal "%s" YIELD Legal.name, Legal.code;`, Table_Space, vid)
  587. resp, err := session.Execute(query)
  588. if err != nil {
  589. return nil, fmt.Errorf("failed to execute fetch query: %w", err)
  590. }
  591. if !resp.IsSucceed() {
  592. return nil, fmt.Errorf("query failed: %s", resp.GetErrorMsg())
  593. }
  594. rows := resp.GetRows()
  595. if len(rows) == 0 {
  596. return nil, nil // 没查到
  597. }
  598. values := rows[0].Values
  599. info := &Legal{}
  600. if values[0].GetSVal() != nil {
  601. info.Name = string(values[0].GetSVal())
  602. }
  603. if values[1].GetSVal() != nil {
  604. info.Code = string(values[1].GetSVal())
  605. }
  606. return info, nil
  607. }
  608. // CheckLegalRelationships3 逻辑正确,但是存咋重复数据,还有数据路径存在包含的关系
  609. func CheckLegalRelationships3(session *nebula.Session, names []string, deep, stype int) (bool, []string, error) {
  610. if len(names) < 2 {
  611. return false, nil, fmt.Errorf("企业数量不足,至少需要两个")
  612. }
  613. var allPaths []string
  614. for i := 0; i < len(names); i++ {
  615. start := names[i]
  616. // 构造剩下的企业列表作为目标
  617. targets := []string{}
  618. for j := 0; j < len(names); j++ {
  619. if i != j {
  620. targets = append(targets, fmt.Sprintf(`"%s"`, names[j]))
  621. }
  622. }
  623. targetList := strings.Join(targets, ", ")
  624. // 构造查询语句
  625. query := fmt.Sprintf(`
  626. USE %s;
  627. MATCH p=(a:Legal{name:"%s"})-[*1..%d]-(b:Legal)
  628. WHERE b.Legal.name IN [%s]
  629. RETURN p LIMIT 1
  630. `, Table_Space, start, deep, targetList)
  631. resp, err := session.Execute(query)
  632. if err != nil {
  633. return false, nil, fmt.Errorf("查询失败: %w", err)
  634. }
  635. if !resp.IsSucceed() {
  636. return false, nil, fmt.Errorf("查询执行失败: %s", resp.GetErrorMsg())
  637. }
  638. if resp.GetRowSize() > 0 {
  639. for _, row := range resp.GetRows() {
  640. if len(row.Values) == 0 {
  641. continue
  642. }
  643. val := row.Values[0]
  644. if !val.IsSetPVal() {
  645. continue
  646. }
  647. path := val.GetPVal()
  648. var builder strings.Builder
  649. // 获取起点名称
  650. curName := ""
  651. srcVertex := path.Src
  652. if srcVertex != nil && srcVertex.Vid != nil && srcVertex.Vid.IsSetSVal() {
  653. vid := string(srcVertex.Vid.GetSVal())
  654. lea, err := getLegalByVid(session, vid)
  655. if err != nil {
  656. log.Println("getLegalByVid err:", err, vid)
  657. } else {
  658. curName = lea.Name
  659. }
  660. }
  661. builder.WriteString(curName)
  662. // 遍历路径步骤,考虑方向
  663. for _, step := range path.Steps {
  664. dstName := ""
  665. if step.Dst != nil && step.Dst.Vid != nil && step.Dst.Vid.IsSetSVal() {
  666. vid := string(step.Dst.Vid.GetSVal())
  667. lea, err := getLegalByVid(session, vid)
  668. if err != nil {
  669. log.Println("getLegalByVid err:", err, vid)
  670. } else {
  671. dstName = lea.Name
  672. }
  673. }
  674. // 判断方向
  675. if step.Type > 0 {
  676. // 正向边:cur → dst
  677. builder.WriteString(" → ")
  678. builder.WriteString(dstName)
  679. curName = dstName
  680. } else if step.Type < 0 {
  681. // 反向边:cur ← dst(即 dst → cur)
  682. builder.WriteString(" ← ")
  683. builder.WriteString(dstName)
  684. curName = dstName
  685. } else {
  686. // 无法判断方向
  687. builder.WriteString(" - ")
  688. builder.WriteString(dstName)
  689. curName = dstName
  690. }
  691. }
  692. strPath := builder.String()
  693. if strings.Count(strPath, "→")+strings.Count(strPath, "←") >= 1 {
  694. allPaths = append(allPaths, strPath)
  695. if stype == 0 {
  696. return true, allPaths, nil
  697. }
  698. }
  699. }
  700. }
  701. }
  702. if len(allPaths) > 0 {
  703. return true, allPaths, nil
  704. }
  705. return false, nil, nil
  706. }
  707. // ----------//
  708. // 判断 pathA 是否是 pathB 的子路径(正向完全包含)
  709. func isSubPath(pathA, pathB []string) bool {
  710. if len(pathA) >= len(pathB) {
  711. return false
  712. }
  713. for i := 0; i <= len(pathB)-len(pathA); i++ {
  714. match := true
  715. for j := 0; j < len(pathA); j++ {
  716. if pathA[j] != pathB[i+j] {
  717. match = false
  718. break
  719. }
  720. }
  721. if match {
  722. return true
  723. }
  724. }
  725. return false
  726. }
  727. // 返回路径唯一 key(方向不敏感)
  728. func generatePathKey(path []string) string {
  729. // 保证方向一致性,字典序小的作为 key
  730. normal := strings.Join(path, "->")
  731. reversed := strings.Join(reverseSlice(path), "->")
  732. if normal < reversed {
  733. return normal
  734. }
  735. return reversed
  736. }
  737. // 反转字符串切片
  738. func reverseSlice(slice []string) []string {
  739. reversed := make([]string, len(slice))
  740. for i, v := range slice {
  741. reversed[len(slice)-1-i] = v
  742. }
  743. return reversed
  744. }
  745. // CheckLegalRelationships4 CheckLegalRelationships4
  746. func CheckLegalRelationships4(session *nebula.Session, names []string, deep, stype int) (bool, []string, error) {
  747. if len(names) < 2 {
  748. return false, nil, fmt.Errorf("企业数量不足,至少需要两个")
  749. }
  750. var rawNodeLists [][]string
  751. for i := 0; i < len(names); i++ {
  752. start := names[i]
  753. // 构造剩下的企业列表作为目标
  754. targets := []string{}
  755. for j := 0; j < len(names); j++ {
  756. if i != j {
  757. targets = append(targets, fmt.Sprintf(`"%s"`, names[j]))
  758. }
  759. }
  760. targetList := strings.Join(targets, ", ")
  761. // 构造查询语句
  762. query := fmt.Sprintf(`
  763. USE %s;
  764. MATCH p=(a:Legal{name:"%s"})-[*1..%d]-(b:Legal)
  765. WHERE b.Legal.name IN [%s]
  766. RETURN p LIMIT 1
  767. `, Table_Space, start, deep, targetList)
  768. resp, err := session.Execute(query)
  769. if err != nil {
  770. return false, nil, fmt.Errorf("查询失败: %w", err)
  771. }
  772. if !resp.IsSucceed() {
  773. return false, nil, fmt.Errorf("查询执行失败: %s", resp.GetErrorMsg())
  774. }
  775. if resp.GetRowSize() > 0 {
  776. for _, row := range resp.GetRows() {
  777. if len(row.Values) == 0 {
  778. continue
  779. }
  780. val := row.Values[0]
  781. if !val.IsSetPVal() {
  782. continue
  783. }
  784. path := val.GetPVal()
  785. var pathNames []string
  786. // 起点
  787. srcVertex := path.Src
  788. if srcVertex != nil && srcVertex.Vid != nil && srcVertex.Vid.IsSetSVal() {
  789. vid := string(srcVertex.Vid.GetSVal())
  790. lea, err := getLegalByVid(session, vid)
  791. if err != nil {
  792. log.Println("getVidByName err", err, vid)
  793. }
  794. if lea != nil && lea.Name != "" {
  795. pathNames = append(pathNames, lea.Name)
  796. }
  797. //pathNames = append(pathNames, lea.Name)
  798. }
  799. // 解析带方向的路径
  800. for _, step := range path.Steps {
  801. dstVertex := step.Dst
  802. if dstVertex != nil && dstVertex.Vid != nil && dstVertex.Vid.IsSetSVal() {
  803. vid := string(dstVertex.Vid.GetSVal())
  804. lea, err := getLegalByVid(session, vid)
  805. if err != nil {
  806. log.Println("getVidByName err", err, vid)
  807. }
  808. if lea != nil && lea.Name != "" {
  809. pathNames = append(pathNames, lea.Name)
  810. }
  811. }
  812. }
  813. if len(pathNames) >= 2 {
  814. rawNodeLists = append(rawNodeLists, pathNames)
  815. if stype == 0 {
  816. return true, []string{strings.Join(pathNames, " → ")}, nil
  817. }
  818. }
  819. }
  820. }
  821. }
  822. // 去除重复、包含的路径(保留最长链条)
  823. uniquePathMap := map[string][]string{}
  824. for _, nodes := range rawNodeLists {
  825. key := generatePathKey(nodes)
  826. shouldAdd := true
  827. for k, existing := range uniquePathMap {
  828. if isSubPath(nodes, existing) || isSubPath(reverseSlice(nodes), existing) {
  829. shouldAdd = false
  830. break
  831. }
  832. if isSubPath(existing, nodes) {
  833. delete(uniquePathMap, k)
  834. }
  835. }
  836. if shouldAdd {
  837. uniquePathMap[key] = nodes
  838. }
  839. }
  840. var result []string
  841. for _, path := range uniquePathMap {
  842. result = append(result, strings.Join(path, " → "))
  843. }
  844. if len(result) > 0 {
  845. return true, result, nil
  846. }
  847. return false, nil, nil
  848. }
  849. func CheckLegalRelationships5(session *nebula.Session, names []string, deep, stype int) (bool, []string, error) {
  850. if len(names) < 2 {
  851. return false, nil, fmt.Errorf("企业数量不足,至少需要两个")
  852. }
  853. var rawPaths []string
  854. var rawNodeLists [][]string
  855. for i := 0; i < len(names); i++ {
  856. start := names[i]
  857. targets := []string{}
  858. for j := 0; j < len(names); j++ {
  859. if i != j {
  860. targets = append(targets, fmt.Sprintf(`"%s"`, names[j]))
  861. }
  862. }
  863. targetList := strings.Join(targets, ", ")
  864. query := fmt.Sprintf(`
  865. USE %s;
  866. MATCH p=(a:Legal{name:"%s"})-[*1..%d]-(b:Legal)
  867. WHERE b.Legal.name IN [%s]
  868. RETURN p LIMIT 1
  869. `, Table_Space, start, deep, targetList)
  870. resp, err := session.Execute(query)
  871. if err != nil {
  872. return false, nil, fmt.Errorf("查询失败: %w", err)
  873. }
  874. if !resp.IsSucceed() {
  875. return false, nil, fmt.Errorf("查询执行失败: %s", resp.GetErrorMsg())
  876. }
  877. if resp.GetRowSize() > 0 {
  878. for _, row := range resp.GetRows() {
  879. if len(row.Values) == 0 {
  880. continue
  881. }
  882. val := row.Values[0]
  883. if !val.IsSetPVal() {
  884. continue
  885. }
  886. path := val.GetPVal()
  887. var builder strings.Builder
  888. var nodeNames []string
  889. curName := ""
  890. srcVertex := path.Src
  891. if srcVertex != nil && srcVertex.Vid != nil && srcVertex.Vid.IsSetSVal() {
  892. vid := string(srcVertex.Vid.GetSVal())
  893. lea, err := getLegalByVid(session, vid)
  894. if err != nil {
  895. log.Println("getLegalByVid err:", err, vid)
  896. } else {
  897. curName = lea.Name
  898. }
  899. }
  900. builder.WriteString(curName)
  901. nodeNames = append(nodeNames, curName)
  902. for _, step := range path.Steps {
  903. dstName := ""
  904. if step.Dst != nil && step.Dst.Vid != nil && step.Dst.Vid.IsSetSVal() {
  905. vid := string(step.Dst.Vid.GetSVal())
  906. lea, err := getLegalByVid(session, vid)
  907. if err != nil {
  908. log.Println("getLegalByVid err:", err, vid)
  909. } else {
  910. if lea != nil && lea.Name != "" {
  911. dstName = lea.Name
  912. }
  913. }
  914. }
  915. if step.Type > 0 {
  916. builder.WriteString(" → ")
  917. } else if step.Type < 0 {
  918. builder.WriteString(" ← ")
  919. } else {
  920. builder.WriteString(" - ")
  921. }
  922. builder.WriteString(dstName)
  923. nodeNames = append(nodeNames, dstName)
  924. }
  925. rawPaths = append(rawPaths, builder.String())
  926. rawNodeLists = append(rawNodeLists, nodeNames)
  927. if stype == 0 {
  928. return true, []string{builder.String()}, nil
  929. }
  930. }
  931. }
  932. }
  933. // 去重 + 保留最长路径
  934. uniqueMap := map[string]string{}
  935. for i, nodes := range rawNodeLists {
  936. pathStr := rawPaths[i]
  937. key := generatePathKey(nodes)
  938. shouldAdd := true
  939. for k, _ := range uniqueMap {
  940. existingNodes := strings.Split(k, "|")
  941. if isSubPath(nodes, existingNodes) || isSubPath(reverseSlice(nodes), existingNodes) {
  942. shouldAdd = false
  943. break
  944. }
  945. if isSubPath(existingNodes, nodes) {
  946. delete(uniqueMap, k)
  947. }
  948. }
  949. if shouldAdd {
  950. uniqueMap[key] = pathStr
  951. }
  952. }
  953. var finalPaths []string
  954. for _, v := range uniqueMap {
  955. finalPaths = append(finalPaths, v)
  956. }
  957. if len(finalPaths) > 0 {
  958. return true, finalPaths, nil
  959. }
  960. return false, nil, nil
  961. }
  962. type GraphNode struct {
  963. Name string `json:"name"`
  964. }
  965. type GraphLink struct {
  966. Source string `json:"source"`
  967. Target string `json:"target"`
  968. Type string `json:"type"` // 可选字段,比如 "投资"
  969. }
  970. type GraphResult struct {
  971. Nodes []GraphNode `json:"nodes"`
  972. Links []GraphLink `json:"links"`
  973. }
  974. func (c *NebulaClient) CheckLegalRelationships(names []string, deep, stype int) (bool, []string, *GraphResult, error) {
  975. if len(names) < 2 {
  976. return false, nil, nil, fmt.Errorf("企业数量不足,至少需要两个")
  977. }
  978. if deep == 0 {
  979. deep = 1
  980. }
  981. var rawPaths []string
  982. var rawNodeLists [][]string
  983. nodeSet := make(map[string]struct{})
  984. graph := &GraphResult{}
  985. for i := 0; i < len(names); i++ {
  986. start := names[i]
  987. targets := []string{}
  988. for j := 0; j < len(names); j++ {
  989. if i != j {
  990. targets = append(targets, fmt.Sprintf(`"%s"`, names[j]))
  991. }
  992. }
  993. targetList := strings.Join(targets, ", ")
  994. query := fmt.Sprintf(`USE %s; MATCH p=(a:Legal{name:"%s"})-[*1..%d]-(b:Legal) WHERE b.Legal.name IN [%s] RETURN p LIMIT 1`, Table_Space, start, deep, targetList)
  995. resp, err := c.ExecuteWithReconnect(query)
  996. if err != nil {
  997. return false, nil, nil, fmt.Errorf("查询失败: %w", err)
  998. }
  999. if resp == nil {
  1000. return false, nil, nil, fmt.Errorf("查询失败,response is nil")
  1001. }
  1002. if !resp.IsSucceed() {
  1003. return false, nil, nil, fmt.Errorf("执行失败: %s", resp.GetErrorMsg())
  1004. }
  1005. if resp.GetRowSize() > 0 {
  1006. for _, row := range resp.GetRows() {
  1007. if len(row.Values) == 0 || !row.Values[0].IsSetPVal() {
  1008. continue
  1009. }
  1010. path := row.Values[0].GetPVal()
  1011. var builder strings.Builder
  1012. var nodeNames []string
  1013. // 起点
  1014. src := path.Src
  1015. curName := ""
  1016. if src != nil && src.Vid != nil && src.Vid.IsSetSVal() {
  1017. vid := string(src.Vid.GetSVal())
  1018. lea, err := getLegalByVid(c.session, vid)
  1019. if err != nil {
  1020. log.Println("getLegalByVid err:", err, vid)
  1021. } else if lea != nil && lea.Name != "" {
  1022. curName = lea.Name
  1023. }
  1024. }
  1025. builder.WriteString(curName)
  1026. nodeNames = append(nodeNames, curName)
  1027. // 图谱节点添加
  1028. if curName != "" {
  1029. if _, ok := nodeSet[curName]; !ok {
  1030. nodeSet[curName] = struct{}{}
  1031. graph.Nodes = append(graph.Nodes, GraphNode{Name: curName})
  1032. }
  1033. }
  1034. // 步长处理
  1035. for _, step := range path.Steps {
  1036. dstName := ""
  1037. if step.Dst != nil && step.Dst.Vid != nil && step.Dst.Vid.IsSetSVal() {
  1038. vid := string(step.Dst.Vid.GetSVal())
  1039. lea, err := getLegalByVid(c.session, vid)
  1040. if err != nil {
  1041. log.Println("getLegalByVid err:", err, vid)
  1042. } else if lea != nil && lea.Name != "" {
  1043. dstName = lea.Name
  1044. }
  1045. }
  1046. if step.Type > 0 {
  1047. builder.WriteString(" → ")
  1048. } else if step.Type < 0 {
  1049. builder.WriteString(" ← ")
  1050. } else {
  1051. builder.WriteString(" - ")
  1052. }
  1053. builder.WriteString(dstName)
  1054. nodeNames = append(nodeNames, dstName)
  1055. // 图谱节点添加
  1056. if dstName != "" {
  1057. if _, ok := nodeSet[dstName]; !ok {
  1058. nodeSet[dstName] = struct{}{}
  1059. graph.Nodes = append(graph.Nodes, GraphNode{Name: dstName})
  1060. }
  1061. }
  1062. // 图谱边添加
  1063. if curName != "" && dstName != "" {
  1064. graph.Links = append(graph.Links, GraphLink{
  1065. Source: curName,
  1066. Target: dstName,
  1067. Type: "投资",
  1068. })
  1069. }
  1070. curName = dstName
  1071. }
  1072. rawPaths = append(rawPaths, builder.String())
  1073. rawNodeLists = append(rawNodeLists, nodeNames)
  1074. if stype == 0 {
  1075. return true, []string{builder.String()}, graph, nil
  1076. }
  1077. }
  1078. }
  1079. }
  1080. // 去重 + 最长路径保留
  1081. uniqueMap := map[string]string{}
  1082. for i, nodes := range rawNodeLists {
  1083. pathStr := rawPaths[i]
  1084. key := generatePathKey(nodes)
  1085. shouldAdd := true
  1086. for k := range uniqueMap {
  1087. existingNodes := strings.Split(k, "|")
  1088. if isSubPath(nodes, existingNodes) || isSubPath(reverseSlice(nodes), existingNodes) {
  1089. shouldAdd = false
  1090. break
  1091. }
  1092. if isSubPath(existingNodes, nodes) {
  1093. delete(uniqueMap, k)
  1094. }
  1095. }
  1096. if shouldAdd {
  1097. uniqueMap[key] = pathStr
  1098. }
  1099. }
  1100. var finalPaths []string
  1101. for _, v := range uniqueMap {
  1102. finalPaths = append(finalPaths, v)
  1103. }
  1104. if len(finalPaths) > 0 {
  1105. return true, finalPaths, graph, nil
  1106. }
  1107. return false, nil, nil, nil
  1108. }