public async Task FixWatchLimits(WatchViewSource watchSource, Requester requester, IdLimiter limiter) { if (limiter.Watches) { if (requester.userId > 0) { var watchSearch = new WatchSearch(); watchSearch.UserIds.Add(requester.userId); watchSearch.Reverse = true; limiter.Limit = (await watchSource.SimpleSearchAsync(watchSearch, q => services.permissions.PermissionWhere(q, requester, Keys.ReadAction))) .Select(x => new IdLimit() { id = x.contentId, min = x.lastNotificationId }).ToList(); } // Just a silly thing to ensure "0" elements still means "no search" (although I hate that old // dicision... even though this one could easily be changed, consistency is better) limiter.Limit.Add(new IdLimit() { id = long.MaxValue }); } }
public override async Task <List <ContentView> > PreparedSearchAsync(ContentSearch search, Requester requester) { var baseResult = await base.PreparedSearchAsync(search, requester); var baseIds = baseResult.Select(x => x.id).ToList(); if (baseIds.Count > 0 && search.IncludeAbout) { //This requires intimate knowledge of how watches work. it's increasing the complexity/dependency, //but at least... I don't know, it's way more performant. Come up with some system perhaps after //you see what you need in other instances. var watches = await watchSource.GroupAsync <EntityRelation, long>( watchSource.SearchIds(new WatchSearch() { ContentIds = baseIds }), watchSource.PermIdSelector); //x => x.entityId2); var comments = await commentSource.GroupAsync <EntityRelation, long>( commentSource.SearchIds(new CommentSearch() { ParentIds = baseIds }), commentSource.PermIdSelector); //x => x.entityId1); var votes = new Dictionary <string, Dictionary <long, SimpleAggregateData> >(); var watchSearch = new WatchSearch(); watchSearch.UserIds.Add(requester.userId); watchSearch.ContentIds.AddRange(baseIds); var userWatching = await watchSource.SimpleSearchAsync(watchSearch); //THIS could be an intensive query! Make sure you check the CPU usage! var voteSearch = new VoteSearch(); voteSearch.UserIds.Add(requester.userId); voteSearch.ContentIds.AddRange(baseIds); var userVotes = await voteSource.SimpleSearchAsync(voteSearch); foreach (var voteWeight in Votes.VoteWeights) { votes.Add(voteWeight.Key, await voteSource.GroupAsync <EntityRelation, long>( voteSource.SearchIds(new VoteSearch() { ContentIds = baseIds, Vote = voteWeight.Key }), voteSource.PermIdSelector)); //x => x.entityId2)); } baseResult.ForEach(x => { if (watches.ContainsKey(x.id)) //-x.id)) { x.about.watches = watches[x.id]; //-x.id]; } if (comments.ContainsKey(x.id)) { x.about.comments = comments[x.id]; } x.about.watching = userWatching.Any(y => y.contentId == x.id); x.about.myVote = userVotes.FirstOrDefault(y => y.contentId == x.id)?.vote; foreach (var voteWeight in Votes.VoteWeights) { x.about.votes.Add(voteWeight.Key, new SimpleAggregateData()); if (votes[voteWeight.Key].ContainsKey(x.id)) //-x.id)) { x.about.votes[voteWeight.Key] = votes[voteWeight.Key][x.id]; //-x.id]; } } }); } else if (!search.IncludeAbout) { //Eventually, about is going away anyway baseResult.ForEach(x => { x.about = null; }); } return(baseResult); }
public override async Task <List <ContentView> > PreparedSearchAsync(ContentSearch search, Requester requester) { string key = JsonSerializer.Serialize(search) + JsonSerializer.Serialize(requester); List <ContentView> baseResult = null; bool includeAbout = search.IncludeAbout != null && search.IncludeAbout.Count > 0; bool useCache = !includeAbout; if (useCache && cache.GetValue(key, ref baseResult)) { return(baseResult); } List <long> baseIds = null; Interlocked.Increment(ref rid); var t = services.timer.StartTimer(""); try { baseResult = await base.PreparedSearchAsync(search, requester); baseIds = baseResult.Select(x => x.id).ToList(); t.Name = $"[{rid}] content PreparedSearchAsync {baseResult.Count} ({string.Join(",", baseIds)})"; } finally { services.timer.EndTimer(t); } if (includeAbout && baseResult.Count > 0) { var desired = search.IncludeAbout.Select(x => x.ToLower()); //TODO: STOP THIS MADNESS!! commentSource.JoinPermissions = false; watchSource.JoinPermissions = false; voteSource.JoinPermissions = false; try { Dictionary <long, SimpleAggregateData> comments = null; Dictionary <long, SimpleAggregateData> watches = null; Dictionary <string, Dictionary <long, SimpleAggregateData> > votes = null; List <WatchView> userWatching = null; List <VoteView> userVotes = null; if (desired.Any(x => x.StartsWith("comment"))) { t = services.timer.StartTimer($"[{rid}] comment pull"); comments = await commentSource.GroupAsync <EntityRelation, long>( await commentSource.GetBaseQuery(new CommentSearch() { ParentIds = baseIds }), g => g.relation, commentSource.PermIdSelector); services.timer.EndTimer(t); } if (desired.Any(x => x.StartsWith("watch"))) { t = services.timer.StartTimer($"[{rid}] watch pull (both)"); //This requires intimate knowledge of how watches work. it's increasing the complexity/dependency, //but at least... I don't know, it's way more performant. Come up with some system perhaps after //you see what you need in other instances. watches = await watchSource.GroupAsync <EntityRelation, long>( await watchSource.SearchIds(new WatchSearch() { ContentIds = baseIds }), watchSource.PermIdSelector); var watchSearch = new WatchSearch(); watchSearch.UserIds.Add(requester.userId); watchSearch.ContentIds.AddRange(baseIds); userWatching = await watchSource.SimpleSearchAsync(watchSearch); services.timer.EndTimer(t); } if (desired.Any(x => x.StartsWith("watch"))) { //THIS could be an intensive query! Make sure you check the CPU usage! t = services.timer.StartTimer($"[{rid}] vote pull"); var voteSearch = new VoteSearch(); voteSearch.UserIds.Add(requester.userId); voteSearch.ContentIds.AddRange(baseIds); userVotes = await voteSource.SimpleSearchAsync(voteSearch); services.timer.EndTimer(t); t = services.timer.StartTimer($"[{rid}] vote aggregate"); votes = new Dictionary <string, Dictionary <long, SimpleAggregateData> >(); foreach (var voteWeight in Votes.VoteWeights) { votes.Add(voteWeight.Key, await voteSource.GroupAsync <EntityRelation, long>( await voteSource.SearchIds(new VoteSearch() { ContentIds = baseIds, Vote = voteWeight.Key }), voteSource.PermIdSelector)); //x => x.entityId2)); } services.timer.EndTimer(t); } baseResult.ForEach(x => { if (watches != null) { if (watches.ContainsKey(x.id)) { x.about.watches = watches[x.id]; } x.about.watching = userWatching.Any(y => y.contentId == x.id); } if (comments != null) { if (comments.ContainsKey(x.id)) { x.about.comments = comments[x.id]; } } if (votes != null) { x.about.myVote = userVotes.FirstOrDefault(y => y.contentId == x.id)?.vote; foreach (var voteWeight in Votes.VoteWeights) { x.about.votes.Add(voteWeight.Key, new SimpleAggregateData()); if (votes[voteWeight.Key].ContainsKey(x.id)) { x.about.votes[voteWeight.Key] = votes[voteWeight.Key][x.id]; } } } }); } finally { commentSource.JoinPermissions = true; watchSource.JoinPermissions = true; voteSource.JoinPermissions = true; } } else { //Eventually, about is going away anyway baseResult.ForEach(x => { x.about = null; }); } if (useCache) { cache.StoreItem(key, baseResult); } return(baseResult); }