/// <summary> /// Gets the first 4 videos related to the specified video. Does not support paging. /// </summary> public override async Task <GetRelatedVideosResponse> GetRelatedVideos(GetRelatedVideosRequest request, ServerCallContext context) { // Lookup the tags for the video PreparedStatement tagsForVideoPrepared = await _statementCache.GetOrAddAsync("SELECT tags FROM videos WHERE videoid = ?"); BoundStatement tagsForVideoBound = tagsForVideoPrepared.Bind(request.VideoId.ToGuid()); RowSet tagRows = await _session.ExecuteAsync(tagsForVideoBound).ConfigureAwait(false); // Start with an empty response var response = new GetRelatedVideosResponse { VideoId = request.VideoId }; Row tagRow = tagRows.SingleOrDefault(); if (tagRow == null) { return(response); } var tagsValue = tagRow.GetValue <IEnumerable <string> >("tags"); var tags = tagsValue?.ToList() ?? new List <string>(); // If there are no tags, we can't find related videos if (tags.Count == 0) { return(response); } var relatedVideos = new Dictionary <Uuid, SuggestedVideoPreview>(); PreparedStatement videosForTagPrepared = await _statementCache.GetOrAddAsync("SELECT * FROM videos_by_tag WHERE tag = ? LIMIT ?"); var inFlightQueries = new List <Task <RowSet> >(); for (var i = 0; i < tags.Count; i++) { // Use the number of results we ultimately want * 2 when querying so that we can account for potentially having to filter // out the video Id we're using as the basis for the query as well as duplicates const int pageSize = RelatedVideosToReturn * 2; // Kick off a query for each tag and track them in the inflight requests list string tag = tags[i]; IStatement query = videosForTagPrepared.Bind(tag, pageSize); inFlightQueries.Add(_session.ExecuteAsync(query)); // Every third query, or if this is the last tag, wait on all the query results if (inFlightQueries.Count == 3 || i == tags.Count - 1) { RowSet[] results = await Task.WhenAll(inFlightQueries).ConfigureAwait(false); foreach (RowSet rowSet in results) { foreach (Row row in rowSet) { SuggestedVideoPreview preview = MapRowToVideoPreview(row); // Skip self if (preview.VideoId.Equals(request.VideoId)) { continue; } // Skip videos we already have in the results if (relatedVideos.ContainsKey(preview.VideoId)) { continue; } // Add to results relatedVideos.Add(preview.VideoId, preview); // If we've got enough, no reason to continue if (relatedVideos.Count >= RelatedVideosToReturn) { break; } } // If we've got enough, no reason to continue if (relatedVideos.Count >= RelatedVideosToReturn) { break; } } // See if we've got enough results now to fulfill our requirement if (relatedVideos.Count >= RelatedVideosToReturn) { break; } // We don't have enough yet, so reset the inflight requests to allow another batch of tags to be queried inFlightQueries.Clear(); } } // Add any videos found to the response and return response.Videos.Add(relatedVideos.Values); return(response); }
/// <summary> /// Gets the first 5 videos related to the specified video. /// </summary> public override async Task <GetRelatedVideosResponse> GetRelatedVideos(GetRelatedVideosRequest request, ServerCallContext context) { // Get REST client for dse-search service Uri searchUri = await GetDseSearchUri().ConfigureAwait(false); IRestClient restClient = _createRestClient(searchUri); // Example request: http://127.0.2.15:8983/solr/killrvideo.videos/mlt?q=videoid%3Asome-uuid&wt=json&indent=true&qt=mlt&mlt.fl=name&mlt.mindf=1&mlt.mintf=1 var solrRequest = new RestRequest("killrvideo.videos/mlt"); solrRequest.AddParameter("q", $"videoid:\"{request.VideoId.Value}\""); solrRequest.AddParameter("wt", "json"); // Paging information int start; if (request.PagingState == null || int.TryParse(request.PagingState, out start) == false) { start = 0; } solrRequest.AddParameter("start", start); solrRequest.AddParameter("rows", request.PageSize); //MLT Fields to Consider solrRequest.AddParameter("mlt.fl", "name,description,tags"); //MLT Minimum Document Frequency - the frequency at which words will be ignored which do not occur in at least this many docs. solrRequest.AddParameter("mlt.mindf", 2); //MLT Minimum Term Frequency - the frequency below which terms will be ignored in the source doc. solrRequest.AddParameter("mlt.mintf", 2); IRestResponse <MLTQueryResult> solrResponse = await restClient.ExecuteTaskAsync <MLTQueryResult>(solrRequest).ConfigureAwait(false); // Start with an empty response var response = new GetRelatedVideosResponse { VideoId = request.VideoId }; // Check for network/timeout errors if (solrResponse.ResponseStatus != ResponseStatus.Completed) { // TODO: Logger.Error(response.ErrorException, "Error while querying Solr video suggestions from {host} for {query}", nodeIp, query); return(response); } // Check for HTTP error codes if (solrResponse.StatusCode != HttpStatusCode.OK) { // TODO: Logger.Error("HTTP status code {code} while querying Solr video suggestions from {host} for {query}", (int) response.StatusCode, nodeIp, query); return(response); } // Success int nextPageStartIndex = solrResponse.Data.Response.Start + solrResponse.Data.Response.Docs.Count; string pagingState = nextPageStartIndex == solrResponse.Data.Response.NumFound ? "" : nextPageStartIndex.ToString(); response.PagingState = pagingState; response.Videos.Add(solrResponse.Data.Response.Docs.Select(doc => new SuggestedVideoPreview { VideoId = doc.VideoId.ToUuid(), AddedDate = doc.AddedDate.ToTimestamp(), Name = doc.Name, PreviewImageLocation = doc.PreviewImageLocation, UserId = doc.UserId.ToUuid() })); return(response); }