@@ -3,6 +3,7 @@ package repository
33import (
44 "context"
55 "errors"
6+ "iter"
67 "time"
78
89 "github.com/generate/selfserve/internal/errs"
@@ -191,6 +192,91 @@ func (r *GuestsRepository) UpdateGuest(ctx context.Context, id string, update *m
191192 return & guest , nil
192193}
193194
195+ const fetchAllGuestDocumentsPageSize = 100
196+
197+ // AllGuestDocuments returns a paginated iterator over every guest document in the
198+ // database. It yields one *models.GuestDocument at a time, fetching the next page
199+ // only when the previous one is exhausted. Stop iterating early by returning false
200+ // from the yield function; the first non-nil error stops iteration and is yielded
201+ // as the second value.
202+ func (r * GuestsRepository ) AllGuestDocuments (ctx context.Context ) iter.Seq2 [* models.GuestDocument , error ] {
203+ return func (yield func (* models.GuestDocument , error ) bool ) {
204+ var cursorName , cursorID string
205+
206+ for {
207+ rows , err := r .db .Query (ctx , `
208+ SELECT
209+ g.id,
210+ gb.hotel_id,
211+ CONCAT_WS(' ', g.first_name, g.last_name) AS full_name,
212+ g.first_name,
213+ g.last_name,
214+ COALESCE(g.preferences, g.first_name) AS preferred_name,
215+ g.email,
216+ g.phone,
217+ g.preferences,
218+ g.notes,
219+ r.floor,
220+ r.room_number,
221+ gb.group_size,
222+ gb.status,
223+ gb.arrival_date,
224+ gb.departure_date
225+ FROM guest_bookings gb
226+ JOIN guests g ON g.id = gb.guest_id
227+ JOIN rooms r ON r.id = gb.room_id
228+ WHERE (
229+ $1::text = ''
230+ OR (CONCAT_WS(' ', g.first_name, g.last_name), g.id::text) > ($1::text, $2::text)
231+ )
232+ ORDER BY CONCAT_WS(' ', g.first_name, g.last_name) ASC, g.id ASC
233+ LIMIT $3
234+ ` , cursorName , cursorID , fetchAllGuestDocumentsPageSize )
235+ if err != nil {
236+ yield (nil , err )
237+ return
238+ }
239+
240+ var page []* models.GuestDocument
241+ for rows .Next () {
242+ var doc models.GuestDocument
243+ if err := rows .Scan (
244+ & doc .ID , & doc .HotelID , & doc .FullName ,
245+ & doc .FirstName , & doc .LastName , & doc .PreferredName ,
246+ & doc .Email , & doc .Phone , & doc .Preferences , & doc .Notes ,
247+ & doc .Floor , & doc .RoomNumber , & doc .GroupSize ,
248+ & doc .BookingStatus , & doc .ArrivalDate , & doc .DepartureDate ,
249+ ); err != nil {
250+ rows .Close ()
251+ yield (nil , err )
252+ return
253+ }
254+ page = append (page , & doc )
255+ }
256+ rows .Close ()
257+
258+ if err := rows .Err (); err != nil {
259+ yield (nil , err )
260+ return
261+ }
262+
263+ for _ , doc := range page {
264+ if ! yield (doc , nil ) {
265+ return
266+ }
267+ }
268+
269+ if len (page ) < fetchAllGuestDocumentsPageSize {
270+ return // last page
271+ }
272+
273+ last := page [len (page )- 1 ]
274+ cursorName = last .FullName
275+ cursorID = last .ID
276+ }
277+ }
278+ }
279+
194280func (r * GuestsRepository ) FindGuestsWithActiveBooking (ctx context.Context , filters * models.GuestFilters ) (* models.GuestPage , error ) {
195281 floorsFilter := filters .Floors
196282 groupSizesFilter := filters .GroupSize
0 commit comments