1// Copyright (C) MongoDB, Inc. 2017-present. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7package driver 8 9import ( 10 "context" 11 "errors" 12 "io" 13 "strings" 14 15 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" 16) 17 18// ListCollectionsBatchCursor is a special batch cursor returned from ListCollections that properly 19// handles current and legacy ListCollections operations. 20type ListCollectionsBatchCursor struct { 21 legacy bool // server version < 3.0 22 bc *BatchCursor 23 currentBatch *bsoncore.DocumentSequence 24 err error 25} 26 27// NewListCollectionsBatchCursor creates a new non-legacy ListCollectionsCursor. 28func NewListCollectionsBatchCursor(bc *BatchCursor) (*ListCollectionsBatchCursor, error) { 29 if bc == nil { 30 return nil, errors.New("batch cursor must not be nil") 31 } 32 return &ListCollectionsBatchCursor{bc: bc, currentBatch: new(bsoncore.DocumentSequence)}, nil 33} 34 35// NewLegacyListCollectionsBatchCursor creates a new legacy ListCollectionsCursor. 36func NewLegacyListCollectionsBatchCursor(bc *BatchCursor) (*ListCollectionsBatchCursor, error) { 37 if bc == nil { 38 return nil, errors.New("batch cursor must not be nil") 39 } 40 return &ListCollectionsBatchCursor{legacy: true, bc: bc, currentBatch: new(bsoncore.DocumentSequence)}, nil 41} 42 43// ID returns the cursor ID for this batch cursor. 44func (lcbc *ListCollectionsBatchCursor) ID() int64 { 45 return lcbc.bc.ID() 46} 47 48// Next indicates if there is another batch available. Returning false does not necessarily indicate 49// that the cursor is closed. This method will return false when an empty batch is returned. 50// 51// If Next returns true, there is a valid batch of documents available. If Next returns false, there 52// is not a valid batch of documents available. 53func (lcbc *ListCollectionsBatchCursor) Next(ctx context.Context) bool { 54 if !lcbc.bc.Next(ctx) { 55 return false 56 } 57 58 if !lcbc.legacy { 59 lcbc.currentBatch.Style = lcbc.bc.currentBatch.Style 60 lcbc.currentBatch.Data = lcbc.bc.currentBatch.Data 61 lcbc.currentBatch.ResetIterator() 62 return true 63 } 64 65 lcbc.currentBatch.Style = bsoncore.SequenceStyle 66 lcbc.currentBatch.Data = lcbc.currentBatch.Data[:0] 67 68 var doc bsoncore.Document 69 for { 70 doc, lcbc.err = lcbc.bc.currentBatch.Next() 71 if lcbc.err != nil { 72 if lcbc.err == io.EOF { 73 lcbc.err = nil 74 break 75 } 76 return false 77 } 78 doc, lcbc.err = lcbc.projectNameElement(doc) 79 if lcbc.err != nil { 80 return false 81 } 82 lcbc.currentBatch.Data = append(lcbc.currentBatch.Data, doc...) 83 } 84 85 return true 86} 87 88// Batch will return a DocumentSequence for the current batch of documents. The returned 89// DocumentSequence is only valid until the next call to Next or Close. 90func (lcbc *ListCollectionsBatchCursor) Batch() *bsoncore.DocumentSequence { return lcbc.currentBatch } 91 92// Server returns a pointer to the cursor's server. 93func (lcbc *ListCollectionsBatchCursor) Server() Server { return lcbc.bc.server } 94 95// Err returns the latest error encountered. 96func (lcbc *ListCollectionsBatchCursor) Err() error { 97 if lcbc.err != nil { 98 return lcbc.err 99 } 100 return lcbc.bc.Err() 101} 102 103// Close closes this batch cursor. 104func (lcbc *ListCollectionsBatchCursor) Close(ctx context.Context) error { return lcbc.bc.Close(ctx) } 105 106// project out the database name for a legacy server 107func (*ListCollectionsBatchCursor) projectNameElement(rawDoc bsoncore.Document) (bsoncore.Document, error) { 108 elems, err := rawDoc.Elements() 109 if err != nil { 110 return nil, err 111 } 112 113 var filteredElems []byte 114 for _, elem := range elems { 115 key := elem.Key() 116 if key != "name" { 117 filteredElems = append(filteredElems, elem...) 118 continue 119 } 120 121 name := elem.Value().StringValue() 122 collName := name[strings.Index(name, ".")+1:] 123 filteredElems = bsoncore.AppendStringElement(filteredElems, "name", collName) 124 } 125 126 var filteredDoc []byte 127 filteredDoc = bsoncore.BuildDocument(filteredDoc, filteredElems) 128 return filteredDoc, nil 129} 130