/// <summary> /// Gets the latest videos added to the site. /// </summary> public async Task<LatestVideos> GetLastestVideos(GetLatestVideos getVideos) { // We may need multiple queries to fill the quota var results = new List<VideoPreview>(); // Generate a list of all the possibly bucket dates by truncating now to the day, then subtracting days from that day // going back as many days as we're allowed to query back DateTimeOffset nowToTheDay = DateTimeOffset.UtcNow.Truncate(TimeSpan.TicksPerDay); var bucketDates = Enumerable.Range(0, MaxDaysInPastForLatestVideos + 1) .Select(day => nowToTheDay.Subtract(TimeSpan.FromDays(day))); // If we're going to include paging parameters for the first query, filter out any bucket dates that are more recent // than the first video on the page bool pageFirstQuery = getVideos.FirstVideoOnPageDate.HasValue && getVideos.FirstVideoOnPageVideoId.HasValue; if (pageFirstQuery) { DateTimeOffset maxBucketToTheDay = getVideos.FirstVideoOnPageDate.Value.Truncate(TimeSpan.TicksPerDay); bucketDates = bucketDates.Where(bucketToTheDay => bucketToTheDay <= maxBucketToTheDay); } DateTimeOffset[] bucketDatesArray = bucketDates.ToArray(); // TODO: Run queries in parallel instead of sequentially? for (var i = 0; i < bucketDatesArray.Length; i++) { int recordsStillNeeded = getVideos.PageSize - results.Count; string bucket = bucketDatesArray[i].ToString("yyyyMMdd"); // If we're processing a paged request, use the appropriate statement PreparedStatement preparedStatement; IStatement boundStatement; if (pageFirstQuery && i == 0) { preparedStatement = await _statementCache.NoContext.GetOrAddAsync( "SELECT * FROM latest_videos WHERE yyyymmdd = ? AND (added_date, videoid) <= (?, ?) LIMIT ?"); boundStatement = preparedStatement.Bind(bucket, getVideos.FirstVideoOnPageDate.Value, getVideos.FirstVideoOnPageVideoId.Value, recordsStillNeeded); } else { preparedStatement = await _statementCache.NoContext.GetOrAddAsync("SELECT * FROM latest_videos WHERE yyyymmdd = ? LIMIT ?"); boundStatement = preparedStatement.Bind(bucket, recordsStillNeeded); } RowSet rows = await _session.ExecuteAsync(boundStatement).ConfigureAwait(false); results.AddRange(rows.Select(MapRowToVideoPreview)); // If we've got all the records we need, we can quit querying if (results.Count >= getVideos.PageSize) break; } return new LatestVideos { Videos = results }; }
/// <summary> /// Gets the latest videos added to the site. /// </summary> public async Task<LatestVideos> GetLastestVideos(GetLatestVideos getVideos) { string[] buckets; int bucketIndex; string rowPagingState; // See if we have paging state from a previous page of videos if (TryParsePagingState(getVideos.PagingState, out buckets, out bucketIndex, out rowPagingState) == false) { // Generate a list of all the possibly bucket dates by truncating now to the day, then subtracting days from that day // going back as many days as we're allowed to query back DateTimeOffset nowToTheDay = DateTimeOffset.UtcNow.Truncate(TimeSpan.TicksPerDay); buckets = Enumerable.Range(0, MaxDaysInPastForLatestVideos + 1) .Select(day => nowToTheDay.Subtract(TimeSpan.FromDays(day)).ToString("yyyyMMdd")) .ToArray(); bucketIndex = 0; rowPagingState = null; } // We may need multiple queries to fill the quota so build up a list of results var results = new List<VideoPreview>(); string nextPageState = null; // TODO: Run queries in parallel? while (bucketIndex < buckets.Length) { int recordsStillNeeded = getVideos.PageSize - results.Count; string bucket = buckets[bucketIndex]; // Get a page of records but don't automatically load more pages when enumerating the RowSet PreparedStatement preparedStatement = await _statementCache.NoContext.GetOrAddAsync("SELECT * FROM latest_videos WHERE yyyymmdd = ?"); IStatement boundStatement = preparedStatement.Bind(bucket) .SetAutoPage(false) .SetPageSize(recordsStillNeeded); // Start from where we left off in this bucket if (string.IsNullOrEmpty(rowPagingState) == false) boundStatement.SetPagingState(Convert.FromBase64String(rowPagingState)); RowSet rows = await _session.ExecuteAsync(boundStatement).ConfigureAwait(false); results.AddRange(rows.Select(MapRowToVideoPreview)); // See if we can stop querying if (results.Count == getVideos.PageSize) { // Are there more rows in the current bucket? if (rows.PagingState != null && rows.PagingState.Length > 0) { // Start from where we left off in this bucket if we get the next page nextPageState = CreatePagingState(buckets, bucketIndex, Convert.ToBase64String(rows.PagingState)); } else if (bucketIndex != buckets.Length - 1) { // Start from the beginning of the next bucket since we're out of rows in this one nextPageState = CreatePagingState(buckets, bucketIndex + 1, string.Empty); } break; } bucketIndex++; } return new LatestVideos { Videos = results, PagingState = nextPageState }; }