@@ -16,6 +16,8 @@ package gcpspanner
1616
1717import (
1818 "context"
19+ "errors"
20+ "fmt"
1921 "time"
2022
2123 "cloud.google.com/go/spanner"
@@ -173,24 +175,61 @@ func (c *Client) GetLatestSavedSearchNotificationEvent(
173175 return r .readRowByKey (ctx , key )
174176}
175177
178+ // savedSearchNotificationEventCursor is used for pagination.
179+ type savedSearchNotificationEventCursor struct {
180+ LastTimestamp time.Time `json:"last_timestamp"`
181+ LastID string `json:"last_id"`
182+ }
183+
184+ // decodeSavedSearchNotificationEventCursor decodes a cursor string.
185+ func decodeSavedSearchNotificationEventCursor (cursor string ) (* savedSearchNotificationEventCursor , error ) {
186+ return decodeCursor [savedSearchNotificationEventCursor ](cursor )
187+ }
188+
189+ // encodeSavedSearchNotificationEventCursor encodes a cursor struct.
190+ func encodeSavedSearchNotificationEventCursor (lastTimestamp time.Time , lastID string ) string {
191+ return encodeCursor (savedSearchNotificationEventCursor {
192+ LastTimestamp : lastTimestamp ,
193+ LastID : lastID ,
194+ })
195+ }
196+
176197func (c * Client ) ListSavedSearchNotificationEvents (ctx context.Context ,
177- savedSearchID string , snapshotType string , limit int ) ([]SavedSearchNotificationEvent , error ) {
198+ savedSearchID string , snapshotType string , pageSize int , pageToken * string ) ([]SavedSearchNotificationEvent , * string , error ) {
199+ var parsedToken * savedSearchNotificationEventCursor
200+ var err error
201+ if pageToken != nil {
202+ parsedToken , err = decodeSavedSearchNotificationEventCursor (* pageToken )
203+ if err != nil {
204+ return nil , nil , errors .Join (ErrInternalQueryFailure , err )
205+ }
206+ }
207+
208+ params := map [string ]any {
209+ "SavedSearchId" : savedSearchID ,
210+ "SnapshotType" : SavedSearchSnapshotType (snapshotType ),
211+ "Limit" : pageSize + 1 ,
212+ }
213+
214+ var pageFilter string
215+ if parsedToken != nil {
216+ pageFilter = `AND (Timestamp < @LastTimestamp OR (Timestamp = @LastTimestamp AND EventId < @LastID))`
217+ params ["LastTimestamp" ] = parsedToken .LastTimestamp
218+ params ["LastID" ] = parsedToken .LastID
219+ }
220+
178221 stmt := spanner.Statement {
179- SQL : `SELECT * FROM SavedSearchNotificationEvents
180- WHERE SavedSearchId = @SavedSearchId AND SnapshotType = @SnapshotType
181- ORDER BY Timestamp DESC
182- LIMIT @Limit` ,
183- Params : map [string ]any {
184- "SavedSearchId" : savedSearchID ,
185- "SnapshotType" : SavedSearchSnapshotType (snapshotType ),
186- "Limit" : limit ,
187- },
222+ SQL : fmt .Sprintf (`SELECT * FROM SavedSearchNotificationEvents
223+ WHERE SavedSearchId = @SavedSearchId AND SnapshotType = @SnapshotType %s
224+ ORDER BY Timestamp DESC, EventId DESC
225+ LIMIT @Limit` , pageFilter ),
226+ Params : params ,
188227 }
189228 iter := c .Single ().Query (ctx , stmt )
190229 defer iter .Stop ()
191230
192231 var events []SavedSearchNotificationEvent
193- err : = iter .Do (func (row * spanner.Row ) error {
232+ err = iter .Do (func (row * spanner.Row ) error {
194233 var e SavedSearchNotificationEvent
195234 if err := row .ToStruct (& e ); err != nil {
196235 return err
@@ -200,8 +239,16 @@ func (c *Client) ListSavedSearchNotificationEvents(ctx context.Context,
200239 return nil
201240 })
202241 if err != nil {
203- return nil , err
242+ return nil , nil , err
243+ }
244+
245+ var newCursor * string
246+ if len (events ) > pageSize {
247+ lastEvent := events [pageSize - 1 ]
248+ generatedCursor := encodeSavedSearchNotificationEventCursor (lastEvent .Timestamp , lastEvent .ID )
249+ newCursor = & generatedCursor
250+ events = events [:pageSize ]
204251 }
205252
206- return events , nil
253+ return events , newCursor , nil
207254}
0 commit comments