Making links into one

consider i have a diagram
Screenshot%20from%202019-02-10%2002-00-27

now there are link between actor_id and test of both groups that are made to look like links between groups now what i want is how can i make them look like one link instead of 2 visually and keep both links in data

I suggest that you make those two links not visible. You can just set that property on the link template. And that you add an unmodeled Link that connects the two Groups.

So programmatically you will still have links between simple nodes and can traverse the graph that way, as specified in the model. But visually the groups will be connected and those links will not be in the model, as you seem to be requiring.

This must be implemented by code that you have to write that creates a new Link (you can just copy an archetype instance of a Link), sets its Link.fromNode and Link.toNode, and then calls Diagram.add.

If your app allows links to be added, reconnected, and removed, then you will want to add and remove such group-connecting links to accurately reflect the presence of any regular but not-visible links between member nodes. I think in this situation you will want to implement Node | GoJS API and linkDisconnected event handlers (just on the simple Nodes) that call Group | GoJS API on any Part.containingGroup to see if any inter-Group Links need to be added or removed.

in this case i will have to traverse whole model json every time diagram is loaded and use diagram.add to add links every time

Yes, but only by traversing the graph of Nodes and Groups and Links, not the model data directly. It’s a lot easier to traverse Nodes and Links because there are lots of properties and methods that make it easier than just looking at plain JavaScript objects.

    // Whenever the user expands or collapses a group, this gets called.
    // You might also want to call it after loading a diagram
    // or adding/ removing links or making programmatic changes.
    function showIntergroupLinks(node) {
      var diagram = node.diagram;
      // remember all summary links, will delete unneeded ones later
      var summaries = new go.Map(go.Link, "number");
      diagram.links.filter(isSummaryLink).each(function(l) { summaries.add(l, 0); } );

      // iterate over all links, and see which ones need summarization
      var newsummaries = new go.Set(go.Link);
      diagram.links.each(function(l) {
        if (l.fromNode === null || l.toNode === null) return;
        //if (l.fromNode.isVisible() && l.toNode.isVisible()) return;
        var fromvis = l.fromNode.findVisibleNode();
        var tovis = l.toNode.findVisibleNode();
        if (fromvis === null && tovis === null) return;
        if ((fromvis instanceof go.Group && !fromvis.isSubGraphExpanded) ||
          (tovis instanceof go.Group && !tovis.isSubGraphExpanded)) {
          l.visible = false;

          // now deal with Groups; if FROMVIS or TOVIS is a regular Node, use its containing Group instead
          var fromgrp = (fromvis instanceof go.Group) ? fromvis : fromvis.containingGroup;
          if (fromgrp === null) return;
          var togrp = (tovis instanceof go.Group) ? tovis : tovis.containingGroup;
          if (togrp === null) return;

          // don't have summaries from nested groups to nested groups within a group
          if (fromgrp === togrp) return;

          // see if there is already a summary link between these two groups, in either direction
          var summlink = null;
          var it = summaries.iterator;
          while (it.next()) {
            var link = it.key;
            if ((link.fromNode === fromgrp && link.toNode === togrp) ||
                (link.fromNode === togrp && link.toNode === fromgrp)) {
              summlink = link;
            }
          }
          if (summlink === null) {  // if not, create it, remember to add it to the diagram, and add to SUMMARIES
            summlink = makeSummaryLink(fromgrp, togrp);
            newsummaries.add(summlink);
            summaries.add(summlink, 1);
          } else {  // increment usage in SUMMARIES
            summaries.set(summlink, summaries.get(summlink) + 1);
          }
        } else {
          l.visible = true;
        }
      })

      // now add any new summary links
      newsummaries.each(function(l) { diagram.add(l); });
      // and remove any unused summary links
      summaries.each(function(kvp) { if (kvp.value === 0) diagram.remove(kvp.key); });
    }

    // we'll implement summary links as unmodeled links -- no link data
    function isSummaryLink(l) { return l.data === null; }

    function makeSummaryLink(group1, group2) {
      // add an unmodeled Link to the Diagram -- Link.data will be null
      var summlink =
        $(go.Link,  // this cannot have any Bindings
          $(go.Shape, { stroke: "red", strokeWidth: 2 })
        );
      summlink.fromNode = group1;
      summlink.toNode = group2;
      return summlink;
    }