|
@@ -606,7 +606,8 @@ RETURN p LIMIT 1
|
|
|
}
|
|
|
|
|
|
if len(pathNames) >= 2 {
|
|
|
- allPaths = append(allPaths, strings.Join(pathNames, " -> "))
|
|
|
+ dd := strings.Join(pathNames, " -> ")
|
|
|
+ allPaths = append(allPaths, dd)
|
|
|
if stype == 0 {
|
|
|
return true, allPaths, nil
|
|
|
}
|
|
@@ -652,3 +653,273 @@ FETCH PROP ON Legal "%s" YIELD Legal.name, Legal.code;`, Table_Space, vid)
|
|
|
}
|
|
|
return info, nil
|
|
|
}
|
|
|
+
|
|
|
+// CheckLegalRelationships3 逻辑正确,但是存咋重复数据,还有数据路径存在包含的关系
|
|
|
+func CheckLegalRelationships3(session *nebula.Session, names []string, deep, stype int) (bool, []string, error) {
|
|
|
+ if len(names) < 2 {
|
|
|
+ return false, nil, fmt.Errorf("企业数量不足,至少需要两个")
|
|
|
+ }
|
|
|
+
|
|
|
+ var allPaths []string
|
|
|
+
|
|
|
+ for i := 0; i < len(names); i++ {
|
|
|
+ start := names[i]
|
|
|
+
|
|
|
+ // 构造剩下的企业列表作为目标
|
|
|
+ targets := []string{}
|
|
|
+ for j := 0; j < len(names); j++ {
|
|
|
+ if i != j {
|
|
|
+ targets = append(targets, fmt.Sprintf(`"%s"`, names[j]))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ targetList := strings.Join(targets, ", ")
|
|
|
+
|
|
|
+ // 构造查询语句
|
|
|
+ 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)
|
|
|
+
|
|
|
+ resp, err := session.Execute(query)
|
|
|
+ if err != nil {
|
|
|
+ return false, nil, fmt.Errorf("查询失败: %w", err)
|
|
|
+ }
|
|
|
+ if !resp.IsSucceed() {
|
|
|
+ return false, nil, fmt.Errorf("查询执行失败: %s", resp.GetErrorMsg())
|
|
|
+ }
|
|
|
+
|
|
|
+ if resp.GetRowSize() > 0 {
|
|
|
+ for _, row := range resp.GetRows() {
|
|
|
+ if len(row.Values) == 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ val := row.Values[0]
|
|
|
+ if !val.IsSetPVal() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ path := val.GetPVal()
|
|
|
+ var builder strings.Builder
|
|
|
+
|
|
|
+ // 获取起点名称
|
|
|
+ curName := ""
|
|
|
+ srcVertex := path.Src
|
|
|
+ if srcVertex != nil && srcVertex.Vid != nil && srcVertex.Vid.IsSetSVal() {
|
|
|
+ vid := string(srcVertex.Vid.GetSVal())
|
|
|
+ lea, err := getLegalByVid(session, vid)
|
|
|
+ if err != nil {
|
|
|
+ log.Println("getLegalByVid err:", err, vid)
|
|
|
+ } else {
|
|
|
+ curName = lea.Name
|
|
|
+ }
|
|
|
+ }
|
|
|
+ builder.WriteString(curName)
|
|
|
+
|
|
|
+ // 遍历路径步骤,考虑方向
|
|
|
+ for _, step := range path.Steps {
|
|
|
+ dstName := ""
|
|
|
+ if step.Dst != nil && step.Dst.Vid != nil && step.Dst.Vid.IsSetSVal() {
|
|
|
+ vid := string(step.Dst.Vid.GetSVal())
|
|
|
+ lea, err := getLegalByVid(session, vid)
|
|
|
+ if err != nil {
|
|
|
+ log.Println("getLegalByVid err:", err, vid)
|
|
|
+ } else {
|
|
|
+ dstName = lea.Name
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断方向
|
|
|
+ if step.Type > 0 {
|
|
|
+ // 正向边:cur → dst
|
|
|
+ builder.WriteString(" → ")
|
|
|
+ builder.WriteString(dstName)
|
|
|
+ curName = dstName
|
|
|
+ } else if step.Type < 0 {
|
|
|
+ // 反向边:cur ← dst(即 dst → cur)
|
|
|
+ builder.WriteString(" ← ")
|
|
|
+ builder.WriteString(dstName)
|
|
|
+ curName = dstName
|
|
|
+ } else {
|
|
|
+ // 无法判断方向
|
|
|
+ builder.WriteString(" - ")
|
|
|
+ builder.WriteString(dstName)
|
|
|
+ curName = dstName
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ strPath := builder.String()
|
|
|
+ if strings.Count(strPath, "→")+strings.Count(strPath, "←") >= 1 {
|
|
|
+ allPaths = append(allPaths, strPath)
|
|
|
+ if stype == 0 {
|
|
|
+ return true, allPaths, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(allPaths) > 0 {
|
|
|
+ return true, allPaths, nil
|
|
|
+ }
|
|
|
+ return false, nil, nil
|
|
|
+}
|
|
|
+
|
|
|
+// ----------//
|
|
|
+// 判断 pathA 是否是 pathB 的子路径(正向完全包含)
|
|
|
+func isSubPath(pathA, pathB []string) bool {
|
|
|
+ if len(pathA) >= len(pathB) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ for i := 0; i <= len(pathB)-len(pathA); i++ {
|
|
|
+ match := true
|
|
|
+ for j := 0; j < len(pathA); j++ {
|
|
|
+ if pathA[j] != pathB[i+j] {
|
|
|
+ match = false
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if match {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+// 返回路径唯一 key(方向不敏感)
|
|
|
+func generatePathKey(path []string) string {
|
|
|
+ // 保证方向一致性,字典序小的作为 key
|
|
|
+ normal := strings.Join(path, "->")
|
|
|
+ reversed := strings.Join(reverseSlice(path), "->")
|
|
|
+ if normal < reversed {
|
|
|
+ return normal
|
|
|
+ }
|
|
|
+ return reversed
|
|
|
+}
|
|
|
+
|
|
|
+// 反转字符串切片
|
|
|
+func reverseSlice(slice []string) []string {
|
|
|
+ reversed := make([]string, len(slice))
|
|
|
+ for i, v := range slice {
|
|
|
+ reversed[len(slice)-1-i] = v
|
|
|
+ }
|
|
|
+ return reversed
|
|
|
+}
|
|
|
+
|
|
|
+// CheckLegalRelationships4 CheckLegalRelationships4
|
|
|
+func CheckLegalRelationships4(session *nebula.Session, names []string, deep, stype int) (bool, []string, error) {
|
|
|
+ if len(names) < 2 {
|
|
|
+ return false, nil, fmt.Errorf("企业数量不足,至少需要两个")
|
|
|
+ }
|
|
|
+
|
|
|
+ var rawNodeLists [][]string
|
|
|
+
|
|
|
+ for i := 0; i < len(names); i++ {
|
|
|
+ start := names[i]
|
|
|
+
|
|
|
+ // 构造剩下的企业列表作为目标
|
|
|
+ targets := []string{}
|
|
|
+ for j := 0; j < len(names); j++ {
|
|
|
+ if i != j {
|
|
|
+ targets = append(targets, fmt.Sprintf(`"%s"`, names[j]))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ targetList := strings.Join(targets, ", ")
|
|
|
+
|
|
|
+ // 构造查询语句
|
|
|
+ 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)
|
|
|
+
|
|
|
+ resp, err := session.Execute(query)
|
|
|
+ if err != nil {
|
|
|
+ return false, nil, fmt.Errorf("查询失败: %w", err)
|
|
|
+ }
|
|
|
+ if !resp.IsSucceed() {
|
|
|
+ return false, nil, fmt.Errorf("查询执行失败: %s", resp.GetErrorMsg())
|
|
|
+ }
|
|
|
+
|
|
|
+ if resp.GetRowSize() > 0 {
|
|
|
+ for _, row := range resp.GetRows() {
|
|
|
+ if len(row.Values) == 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ val := row.Values[0]
|
|
|
+ if !val.IsSetPVal() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ path := val.GetPVal()
|
|
|
+ var pathNames []string
|
|
|
+
|
|
|
+ // 起点
|
|
|
+ srcVertex := path.Src
|
|
|
+ if srcVertex != nil && srcVertex.Vid != nil && srcVertex.Vid.IsSetSVal() {
|
|
|
+ vid := string(srcVertex.Vid.GetSVal())
|
|
|
+ lea, err := getLegalByVid(session, vid)
|
|
|
+ if err != nil {
|
|
|
+ log.Println("getVidByName err", err, vid)
|
|
|
+ }
|
|
|
+ pathNames = append(pathNames, lea.Name)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析带方向的路径
|
|
|
+ for _, step := range path.Steps {
|
|
|
+ dstVertex := step.Dst
|
|
|
+ if dstVertex != nil && dstVertex.Vid != nil && dstVertex.Vid.IsSetSVal() {
|
|
|
+ vid := string(dstVertex.Vid.GetSVal())
|
|
|
+ lea, err := getLegalByVid(session, vid)
|
|
|
+ if err != nil {
|
|
|
+ log.Println("getVidByName err", err, vid)
|
|
|
+ }
|
|
|
+ pathNames = append(pathNames, lea.Name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(pathNames) >= 2 {
|
|
|
+ rawNodeLists = append(rawNodeLists, pathNames)
|
|
|
+ if stype == 0 {
|
|
|
+ return true, []string{strings.Join(pathNames, " → ")}, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 去除重复、包含的路径(保留最长链条)
|
|
|
+ uniquePathMap := map[string][]string{}
|
|
|
+
|
|
|
+ for _, nodes := range rawNodeLists {
|
|
|
+ key := generatePathKey(nodes)
|
|
|
+
|
|
|
+ shouldAdd := true
|
|
|
+ for k, existing := range uniquePathMap {
|
|
|
+ if isSubPath(nodes, existing) || isSubPath(reverseSlice(nodes), existing) {
|
|
|
+ shouldAdd = false
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if isSubPath(existing, nodes) {
|
|
|
+ delete(uniquePathMap, k)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if shouldAdd {
|
|
|
+ uniquePathMap[key] = nodes
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var result []string
|
|
|
+ for _, path := range uniquePathMap {
|
|
|
+ result = append(result, strings.Join(path, " → "))
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(result) > 0 {
|
|
|
+ return true, result, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return false, nil, nil
|
|
|
+}
|