github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/query/recurse.go (about) 1 /* 2 * Copyright 2017-2018 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package query 18 19 import ( 20 "context" 21 "fmt" 22 "math" 23 24 "github.com/dgraph-io/dgraph/algo" 25 "github.com/dgraph-io/dgraph/x" 26 "github.com/pkg/errors" 27 ) 28 29 func (start *SubGraph) expandRecurse(ctx context.Context, maxDepth uint64) error { 30 // Note: Key format is - "attr|fromUID|toUID" 31 reachMap := make(map[string]struct{}) 32 allowLoop := start.Params.RecurseArgs.AllowLoop 33 var numEdges uint64 34 var exec []*SubGraph 35 var err error 36 37 rrch := make(chan error, len(exec)) 38 startChildren := make([]*SubGraph, len(start.Children)) 39 copy(startChildren, start.Children) 40 // Empty children before giving to ProcessGraph as we are only concerned with DestUids. 41 start.Children = start.Children[:0] 42 43 // Process the root first. 44 go ProcessGraph(ctx, start, nil, rrch) 45 select { 46 case err = <-rrch: 47 if err != nil { 48 return err 49 } 50 case <-ctx.Done(): 51 return ctx.Err() 52 } 53 54 if start.UnknownAttr { 55 return nil 56 } 57 58 // Add children back and expand if necessary 59 if exec, err = expandChildren(ctx, start, startChildren); err != nil { 60 return err 61 } 62 63 dummy := &SubGraph{} 64 var depth uint64 65 for { 66 if depth >= maxDepth { 67 return nil 68 } 69 depth++ 70 71 // When the maximum depth has been reached, avoid retrieving any facets as 72 // the nodes at the other end of the edge will not be a part of this query. 73 // Otherwise, the facets will be included in the query without any other 74 // information about the node, which is quite counter-intuitive. 75 if depth == maxDepth { 76 for _, sg := range exec { 77 sg.Params.Facet = nil 78 } 79 } 80 81 rrch := make(chan error, len(exec)) 82 for _, sg := range exec { 83 go ProcessGraph(ctx, sg, dummy, rrch) 84 } 85 86 var recurseErr error 87 for range exec { 88 select { 89 case err = <-rrch: 90 if err != nil { 91 if recurseErr == nil { 92 recurseErr = err 93 } 94 } 95 case <-ctx.Done(): 96 if recurseErr == nil { 97 recurseErr = ctx.Err() 98 } 99 } 100 } 101 102 if recurseErr != nil { 103 return recurseErr 104 } 105 106 for _, sg := range exec { 107 // sg.uidMatrix can be empty. Continue if that is the case. 108 if len(sg.uidMatrix) == 0 { 109 continue 110 } 111 112 if sg.UnknownAttr { 113 continue 114 } 115 116 if len(sg.Filters) > 0 { 117 // We need to do this in case we had some filters. 118 sg.updateUidMatrix() 119 } 120 121 for mIdx, fromUID := range sg.SrcUIDs.Uids { 122 if allowLoop { 123 for _, ul := range sg.uidMatrix { 124 numEdges += uint64(len(ul.Uids)) 125 } 126 } else { 127 algo.ApplyFilter(sg.uidMatrix[mIdx], func(uid uint64, i int) bool { 128 key := fmt.Sprintf("%s|%d|%d", sg.Attr, fromUID, uid) 129 _, seen := reachMap[key] // Combine fromUID here. 130 if seen { 131 return false 132 } 133 // Mark this edge as taken. We'd disallow this edge later. 134 reachMap[key] = struct{}{} 135 numEdges++ 136 return true 137 }) 138 } 139 } 140 if len(sg.Params.Order) > 0 || len(sg.Params.FacetOrder) > 0 { 141 // Can't use merge sort if the UIDs are not sorted. 142 sg.updateDestUids() 143 } else { 144 sg.DestUIDs = algo.MergeSorted(sg.uidMatrix) 145 } 146 } 147 148 // modify the exec and attach child nodes. 149 var out []*SubGraph 150 var exp []*SubGraph 151 for _, sg := range exec { 152 if sg.UnknownAttr { 153 continue 154 } 155 if len(sg.DestUIDs.Uids) == 0 { 156 continue 157 } 158 if exp, err = expandChildren(ctx, sg, startChildren); err != nil { 159 return err 160 } 161 out = append(out, exp...) 162 } 163 164 if numEdges > x.Config.QueryEdgeLimit { 165 // If we've seen too many edges, stop the query. 166 return errors.Errorf("Exceeded query edge limit = %v. Found %v edges.", 167 x.Config.QueryEdgeLimit, numEdges) 168 } 169 170 if len(out) == 0 { 171 return nil 172 } 173 exec = out 174 } 175 } 176 177 // expandChildren adds child nodes to a SubGraph with no children, expanding them if necessary. 178 func expandChildren(ctx context.Context, sg *SubGraph, children []*SubGraph) ([]*SubGraph, error) { 179 if len(sg.Children) > 0 { 180 return nil, errors.New("Subgraph should not have any children") 181 } 182 // Add children and expand if necessary 183 sg.Children = append(sg.Children, children...) 184 expandedChildren, err := expandSubgraph(ctx, sg) 185 if err != nil { 186 return nil, err 187 } 188 out := make([]*SubGraph, 0, len(expandedChildren)) 189 sg.Children = sg.Children[:0] 190 // Link new child nodes back to parent destination UIDs 191 for _, child := range expandedChildren { 192 newChild := new(SubGraph) 193 newChild.copyFiltersRecurse(child) 194 newChild.SrcUIDs = sg.DestUIDs 195 newChild.Params.Var = child.Params.Var 196 sg.Children = append(sg.Children, newChild) 197 out = append(out, newChild) 198 } 199 return out, nil 200 } 201 202 func recurse(ctx context.Context, sg *SubGraph) error { 203 if !sg.Params.Recurse { 204 return errors.Errorf("Invalid recurse path query") 205 } 206 207 depth := sg.Params.RecurseArgs.Depth 208 if depth == 0 { 209 if sg.Params.RecurseArgs.AllowLoop { 210 return errors.Errorf("Depth must be > 0 when loop is true for recurse query") 211 } 212 // If no depth is specified, expand till we reach all leaf nodes 213 // or we see reach too many nodes. 214 depth = math.MaxUint64 215 } 216 217 for _, child := range sg.Children { 218 if len(child.Children) > 0 { 219 return errors.Errorf( 220 "recurse queries require that all predicates are specified in one level") 221 } 222 } 223 224 return sg.expandRecurse(ctx, depth) 225 }