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 }