@@ -222,6 +222,62 @@ func (c *DatabaseCollection) GetDocSyncData(ctx context.Context, docid string) (
222222
223223}
224224
225+ type ChannelHistory map [string ]map [uint64 ]struct {}
226+
227+ func (ch ChannelHistory ) addChannelHistoryEntry (name string , seq uint64 ) {
228+ if _ , ok := ch [name ]; ! ok {
229+ ch [name ] = make (map [uint64 ]struct {})
230+ }
231+ if _ , ok := ch [name ][seq ]; ! ok {
232+ ch [name ][seq ] = struct {}{}
233+ }
234+ }
235+
236+ func (ch ChannelHistory ) getChannelHistoryAsMap () map [string ][]uint64 {
237+ response := make (map [string ][]uint64 )
238+ for chanName , chanEntry := range ch {
239+ response [chanName ] = make ([]uint64 , 0 )
240+ for seq , _ := range chanEntry {
241+ response [chanName ] = append (response [chanName ], seq )
242+ }
243+ slices .Sort (response [chanName ])
244+ slices .Reverse (response [chanName ])
245+ }
246+ return response
247+ }
248+
249+ // GetDocChannelHistory returns the channel revocation history for the given document as a map
250+ // from channel name to the sequences at which the document was removed from that channel.
251+ // It collects revocation sequences from the active Channels map, the ChannelSet, and the
252+ // ChannelSetHistory (overflow). Only channels that have been revoked at least once appear in
253+ // the result; active memberships with no revocation history are omitted, even though a currently
254+ // assigned channel can still appear if it was revoked and later re-added.
255+ func (c * DatabaseCollection ) GetDocChannelHistory (ctx context.Context , docid string ) (map [string ][]uint64 , error ) {
256+
257+ chanHistory := make (ChannelHistory )
258+ syncData , err := c .GetDocSyncData (ctx , docid )
259+ if err != nil {
260+ return nil , err
261+ }
262+ for chanName , chanVal := range syncData .Channels {
263+ if chanVal != nil && chanVal .Seq != 0 {
264+ chanHistory .addChannelHistoryEntry (chanName , chanVal .Seq )
265+ }
266+ }
267+ for _ , chanSetEntry := range syncData .ChannelSet {
268+ if chanSetEntry .End != 0 {
269+ chanHistory .addChannelHistoryEntry (chanSetEntry .Name , chanSetEntry .End )
270+ }
271+ }
272+ for _ , chanSetEntry := range syncData .ChannelSetHistory {
273+ if chanSetEntry .End != 0 {
274+ chanHistory .addChannelHistoryEntry (chanSetEntry .Name , chanSetEntry .End )
275+ }
276+ }
277+
278+ return chanHistory .getChannelHistoryAsMap (), nil
279+ }
280+
225281// CompactDocChannelHistory removes channel history entries that ended at or before the given sequence number.
226282// This is used to prune stale channel assignment history to reduce storage overhead.
227283func (c * DatabaseCollection ) CompactDocChannelHistory (ctx context.Context , docid string , seq uint64 ) ([]string , error ) {
@@ -258,27 +314,27 @@ func (c *DatabaseCollection) CompactDocChannelHistory(ctx context.Context, docid
258314 cas = doc .Cas
259315 }
260316
261- compactedChannels := make ([] string , 0 )
317+ compactedChannels := make (base. Set )
262318
263319 doc .SyncData .ChannelSetHistory = slices .DeleteFunc (doc .SyncData .ChannelSetHistory , func (channel ChannelSetEntry ) bool {
264320 del := channel .End <= seq
265321 if del {
266- compactedChannels = append ( compactedChannels , channel .Name )
322+ compactedChannels . Add ( channel .Name )
267323 }
268324 return del
269325 })
270326
271327 doc .SyncData .ChannelSet = slices .DeleteFunc (doc .SyncData .ChannelSet , func (channel ChannelSetEntry ) bool {
272328 del := channel .End != 0 && channel .End <= seq
273329 if del {
274- compactedChannels = append ( compactedChannels , channel .Name )
330+ compactedChannels . Add ( channel .Name )
275331 }
276332 return del
277333 })
278334
279335 for chanName , chanEntry := range doc .SyncData .Channels {
280336 if chanEntry != nil && chanEntry .Seq <= seq {
281- compactedChannels = append ( compactedChannels , chanName )
337+ compactedChannels . Add ( chanName )
282338 delete (doc .SyncData .Channels , chanName )
283339 }
284340 }
@@ -320,7 +376,9 @@ func (c *DatabaseCollection) CompactDocChannelHistory(ctx context.Context, docid
320376 base .MouXattrName : rawMouXattr ,
321377 }
322378 _ , err = c .dataStore .UpdateXattrs (ctx , key , 0 , cas , updatedXattr , opts )
323- return compactedChannels , err
379+ compactedChannelArray := compactedChannels .ToArray ()
380+ slices .Sort (compactedChannelArray )
381+ return compactedChannelArray , err
324382}
325383
326384// unmarshalDocumentWithXattrs populates individual xattrs on unmarshalDocumentWithXattrs from a provided xattrs map
0 commit comments