@@ -2,8 +2,6 @@ package query
22
33import (
44 "github.com/google/uuid"
5-
6- "github.com/authzed/spicedb/pkg/spiceerrors"
75)
86
97// Intersection the set of paths that are in all of underlying subiterators.
@@ -221,7 +219,92 @@ func (i *Intersection) IterSubjectsImpl(ctx *Context, resource Object) (PathSeq,
221219}
222220
223221func (i * Intersection ) IterResourcesImpl (ctx * Context , subject ObjectAndRelation ) (PathSeq , error ) {
224- return nil , spiceerrors .MustBugf ("unimplemented: intersection.go IterResourcesImpl" )
222+ ctx .TraceStep (i , "iterating resources for subject %s:%s from %d sub-iterators" , subject .ObjectType , subject .ObjectID , len (i .subIts ))
223+
224+ // Track paths by resource key for combining with AND logic
225+ pathsByKey := make (map [string ]Path )
226+
227+ for iterIdx , it := range i .subIts {
228+ ctx .TraceStep (i , "processing sub-iterator %d" , iterIdx )
229+
230+ pathSeq , err := ctx .IterResources (it , subject )
231+ if err != nil {
232+ return nil , err
233+ }
234+ paths , err := CollectAll (pathSeq )
235+ if err != nil {
236+ return nil , err
237+ }
238+
239+ ctx .TraceStep (i , "sub-iterator %d returned %d paths" , iterIdx , len (paths ))
240+
241+ if len (paths ) == 0 {
242+ ctx .TraceStep (i , "sub-iterator %d returned empty, short-circuiting" , iterIdx )
243+ return EmptyPathSeq (), nil
244+ }
245+
246+ if iterIdx == 0 {
247+ // First iterator - initialize pathsByKey using resource-based keys
248+ for _ , path := range paths {
249+ key := path .Resource .Key ()
250+ if existing , exists := pathsByKey [key ]; ! exists {
251+ pathsByKey [key ] = path
252+ } else {
253+ // If multiple paths for same resource in first iterator, merge with OR
254+ merged , err := existing .MergeOr (path )
255+ if err != nil {
256+ return nil , err
257+ }
258+ pathsByKey [key ] = merged
259+ }
260+ }
261+ } else {
262+ // Subsequent iterators - intersect based on resources and combine caveats
263+ newPathsByKey := make (map [string ]Path )
264+
265+ // First collect all paths from this iterator by resource
266+ currentIterPaths := make (map [string ]Path )
267+ for _ , path := range paths {
268+ key := path .Resource .Key ()
269+ if existing , exists := currentIterPaths [key ]; ! exists {
270+ currentIterPaths [key ] = path
271+ } else {
272+ // Multiple paths for same resource in current iterator, merge with OR
273+ merged , err := existing .MergeOr (path )
274+ if err != nil {
275+ return nil , err
276+ }
277+ currentIterPaths [key ] = merged
278+ }
279+ }
280+
281+ // Now intersect: only keep subjects that exist in both previous and current
282+ for key , currentPath := range currentIterPaths {
283+ if existing , exists := pathsByKey [key ]; exists {
284+ // Combine using intersection logic (AND)
285+ combined , err := existing .MergeAnd (currentPath )
286+ if err != nil {
287+ return nil , err
288+ }
289+ newPathsByKey [key ] = combined
290+ }
291+ // If resource not in previous results, it's filtered out (intersection)
292+ }
293+ pathsByKey = newPathsByKey
294+
295+ if len (pathsByKey ) == 0 {
296+ return EmptyPathSeq (), nil
297+ }
298+ }
299+ }
300+
301+ return func (yield func (Path , error ) bool ) {
302+ for _ , path := range pathsByKey {
303+ if ! yield (path , nil ) {
304+ return
305+ }
306+ }
307+ }, nil
225308}
226309
227310func (i * Intersection ) Clone () Iterator {
0 commit comments