github.com/cayleygraph/cayley@v0.7.7/graph/iterator/query_shape.go (about)

     1  // Copyright 2014 The Cayley Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package iterator
    16  
    17  import (
    18  	"github.com/cayleygraph/cayley/graph"
    19  	"github.com/cayleygraph/quad"
    20  )
    21  
    22  type Node struct {
    23  	ID         int      `json:"id"`
    24  	Tags       []string `json:"tags,omitempty"`
    25  	Values     []string `json:"values,omitempty"`
    26  	IsLinkNode bool     `json:"is_link_node"`
    27  	IsFixed    bool     `json:"is_fixed"`
    28  }
    29  
    30  type Link struct {
    31  	Source   int `json:"source"`
    32  	Target   int `json:"target"`
    33  	Pred     int `json:"type"`
    34  	LinkNode int `json:"link_node"`
    35  }
    36  
    37  type queryShape struct {
    38  	nodes    []Node
    39  	links    []Link
    40  	qs       graph.Namer
    41  	nodeID   int
    42  	hasaIDs  []int
    43  	hasaDirs []quad.Direction
    44  }
    45  
    46  func OutputQueryShapeForIterator(it graph.Iterator, qs graph.Namer, outputMap map[string]interface{}) {
    47  	s := &queryShape{
    48  		qs:     qs,
    49  		nodeID: 1,
    50  	}
    51  
    52  	node := s.MakeNode(it)
    53  	s.AddNode(node)
    54  	outputMap["nodes"] = s.nodes
    55  	outputMap["links"] = s.links
    56  }
    57  
    58  func (s *queryShape) AddNode(n *Node) {
    59  	s.nodes = append(s.nodes, *n)
    60  }
    61  
    62  func (s *queryShape) AddLink(l *Link) {
    63  	s.links = append(s.links, *l)
    64  }
    65  
    66  func (s *queryShape) LastHasa() (int, quad.Direction) {
    67  	return s.hasaIDs[len(s.hasaIDs)-1], s.hasaDirs[len(s.hasaDirs)-1]
    68  }
    69  
    70  func (s *queryShape) PushHasa(i int, d quad.Direction) {
    71  	s.hasaIDs = append(s.hasaIDs, i)
    72  	s.hasaDirs = append(s.hasaDirs, d)
    73  }
    74  
    75  func (s *queryShape) RemoveHasa() {
    76  	s.hasaIDs = s.hasaIDs[:len(s.hasaIDs)-1]
    77  	s.hasaDirs = s.hasaDirs[:len(s.hasaDirs)-1]
    78  }
    79  
    80  func (s *queryShape) StealNode(left *Node, right *Node) {
    81  	for _, v := range right.Values {
    82  		left.Values = append(left.Values, v)
    83  	}
    84  	for _, v := range right.Tags {
    85  		left.Tags = append(left.Tags, v)
    86  	}
    87  	left.IsLinkNode = left.IsLinkNode || right.IsLinkNode
    88  	left.IsFixed = left.IsFixed || right.IsFixed
    89  	for i, link := range s.links {
    90  		rewrite := false
    91  		if link.LinkNode == right.ID {
    92  			link.LinkNode = left.ID
    93  			rewrite = true
    94  		}
    95  		if link.Source == right.ID {
    96  			link.Source = left.ID
    97  			rewrite = true
    98  		}
    99  		if link.Target == right.ID {
   100  			link.Target = left.ID
   101  			rewrite = true
   102  		}
   103  		if rewrite {
   104  			s.links = append(append(s.links[:i], s.links[i+1:]...), link)
   105  		}
   106  	}
   107  }
   108  
   109  func (s *queryShape) MakeNode(it graph.Iterator) *Node {
   110  	n := Node{ID: s.nodeID}
   111  	return s.makeNode(&n, it)
   112  }
   113  func (s *queryShape) makeNode(n *Node, it graph.Iterator) *Node {
   114  	if tg, ok := it.(graph.Tagger); ok {
   115  		for _, tag := range tg.Tags() {
   116  			n.Tags = append(n.Tags, tag)
   117  		}
   118  		for k := range tg.FixedTags() {
   119  			n.Tags = append(n.Tags, k)
   120  		}
   121  		if sub := tg.SubIterators(); len(sub) == 1 {
   122  			return s.makeNode(n, sub[0])
   123  		}
   124  	}
   125  
   126  	switch it := it.(type) {
   127  	case *And:
   128  		for _, sub := range it.SubIterators() {
   129  			s.nodeID++
   130  			newNode := s.MakeNode(sub)
   131  			if _, ok := sub.(*Or); !ok {
   132  				s.StealNode(n, newNode)
   133  			} else {
   134  				s.AddNode(newNode)
   135  				s.AddLink(&Link{n.ID, newNode.ID, 0, 0})
   136  			}
   137  		}
   138  	case *Fixed:
   139  		n.IsFixed = true
   140  		for _, v := range it.Values() {
   141  			n.Values = append(n.Values, s.qs.NameOf(v).String())
   142  		}
   143  	case *HasA:
   144  		hasa := it
   145  		s.PushHasa(n.ID, hasa.it.dir)
   146  		s.nodeID++
   147  		newNode := s.MakeNode(graph.AsLegacy(hasa.it.primary))
   148  		s.AddNode(newNode)
   149  		s.RemoveHasa()
   150  	case *Or:
   151  		for _, sub := range it.SubIterators() {
   152  			s.nodeID++
   153  			newNode := s.MakeNode(sub)
   154  			if _, ok := sub.(*Or); ok {
   155  				s.StealNode(n, newNode)
   156  			} else {
   157  				s.AddNode(newNode)
   158  				s.AddLink(&Link{n.ID, newNode.ID, 0, 0})
   159  			}
   160  		}
   161  	case *LinksTo:
   162  		n.IsLinkNode = true
   163  		lto := it
   164  		s.nodeID++
   165  		newNode := s.MakeNode(graph.AsLegacy(lto.it.primary))
   166  		hasaID, hasaDir := s.LastHasa()
   167  		if (hasaDir == quad.Subject && lto.it.dir == quad.Object) ||
   168  			(hasaDir == quad.Object && lto.it.dir == quad.Subject) {
   169  			s.AddNode(newNode)
   170  			if hasaDir == quad.Subject {
   171  				s.AddLink(&Link{hasaID, newNode.ID, 0, n.ID})
   172  			} else {
   173  				s.AddLink(&Link{newNode.ID, hasaID, 0, n.ID})
   174  			}
   175  		} else if _, ok := graph.AsLegacy(lto.it.primary).(*Fixed); ok {
   176  			s.StealNode(n, newNode)
   177  		} else {
   178  			s.AddNode(newNode)
   179  		}
   180  	}
   181  	return n
   182  }