public async Task <IActionResult> Get( [FromQuery] MiniSearchModel search = null, [FromQuery] int pageSize = 21, [FromQuery] int pageIndex = 1, [FromQuery] int creatorId = 0) { //TODO: Move creator into MiniSearchModel? Creator creatorInfo = new Creator(); if (creatorId > 0) { creatorInfo = await _context.Mini.AsNoTracking().TagWith("Minis API Search") .Include(m => m.Creator) .Select(m => m.Creator) .FirstOrDefaultAsync(c => c.ID == creatorId); } PageInfo pagingInfo = new PageInfo(pageSize, pageIndex); MiniSearchRequest searchRequest = new MiniSearchRequest { PageInfo = pagingInfo, Creator = creatorInfo }; _mapper.Map(search).Over(searchRequest); PaginatedList <Mini> searchResult = await _mediator.Send(searchRequest); _telemetry.TrackEvent("MiniSearchAPI", new Dictionary <string, string> { { "SearchString", searchRequest.SearchString }, { "Tags", String.Join(",", searchRequest.Tags) }, { "FreeOnly", searchRequest.FreeOnly.ToString() }, { "HadResults", searchResult.Count > 0 ? "True" : "False" }, { "PageIndex", searchRequest.PageInfo.PageIndex.ToString() }, { "SortType", searchRequest.SortType } }); return(Ok( searchResult.Select( m => new { ID = m.ID, Name = m.Name, Status = m.Status.ToString(), Creator = new { name = m.Creator.Name, id = m.Creator.ID }, Thumbnail = m.Thumbnail.Replace("miniindex.blob.core.windows.net", _configuration["CDNURL"] + ".azureedge.net"), LinuxTime = m.ApprovedLinuxTime() } ) )); }
public async Task <IActionResult> BrowseMinis( [FromQuery] MiniSearchModel search = null, [FromQuery] int pageSize = 21, [FromQuery] int pageIndex = 1) { //Mild hack - There's some case where pageIndex is hitting 0 and I can't tell how/why. (GitHub #182) if (pageIndex == 0) { pageIndex = 1; } //I really don't want people manually setting massive page sizes, so hardcoding this for now. pageSize = 21; PageInfo pagingInfo = new PageInfo(pageSize, pageIndex); MiniSearchRequest searchRequest = new MiniSearchRequest { PageInfo = pagingInfo }; _mapper.Map(search).Over(searchRequest); PaginatedList <Mini> searchResult = await _mediator.Send(searchRequest); BrowseModel model = new BrowseModel(search, searchResult); _telemetry.TrackEvent("NewMiniSearch", new Dictionary <string, string> { { "SearchString", searchRequest.SearchString }, { "Tags", String.Join(",", searchRequest.Tags) }, { "FreeOnly", searchRequest.FreeOnly.ToString() }, { "HadResults", searchResult.Count > 0 ? "True" : "False" }, { "PageIndex", searchRequest.PageInfo.PageIndex.ToString() }, { "SortType", searchRequest.SortType } }); return(View("BrowseMinis", model)); }
public async Task <PaginatedList <Mini> > Handle(MiniSearchRequest request, CancellationToken cancellationToken) { /* * * Section 1: No filtering, most recently approved first, then IDs * */ IQueryable <Mini> search = _context .Set <Mini>() .Include(m => m.Creator) .OrderByDescending(m => m.ApprovedTime) .ThenByDescending(m => m.ID); /* * * Section 2: Basic filtering on creator and Free Only * */ if (request.Creator != null && request.Creator.ID > 0) { search = search.Where(m => m.Creator == request.Creator); } else { search = search.Where(m => (m.Status == Status.Approved || m.Status == Status.Pending)); } if (request.FreeOnly) { search = search.Where(m => m.Cost == 0); } //TODO: Move this section lower //TODO: Tag status needs to be approved lol /* * * Section 3: Only Minis with the given tag(s) * */ foreach (var tag in request.Tags) { search = search .Where(m => m.MiniTags .Where(x => x.Status == Status.Approved) .Select(x => x.Tag) .Any(t => t.TagName == tag) ); } /* * * Section 4: Analyze the search terms * */ //TODO: If SearchString can be deconstructed into tags, do it and then do a tag search (swap this section and the one above). /* * * Section 5: Text search * */ /* * * Section 6: Sort each result set * */ /* * * Section 7: Concat the two sets of results * */ if (!String.IsNullOrEmpty(request.SearchString)) { //TODO: Also include pairs of words here. string[] searchTerms = request.SearchString .Split(" ", StringSplitOptions.RemoveEmptyEntries) .Distinct() .Select(s => s.Trim().ToUpperInvariant()) .Where(s => !String.IsNullOrEmpty(s)) .ToArray(); IQueryable <Mini> tagSearch = search; //TODO: Move sort to end foreach (string term in searchTerms) { search = search .Where(m => m.Name.Contains(term)) .OrderByDescending(m => m.Name.ToUpper().Equals(term)) // match where the term *is* the model name .ThenByDescending(m => m.Name.ToUpper().StartsWith($"{term} ")) // \ .ThenByDescending(m => m.Name.ToUpper().Contains($" {term} ")) // -- these three lines do whole-word matching; there's a more concise way, but not if we want it to translate to SQL .ThenByDescending(m => m.Name.ToUpper().EndsWith($" {term}")) // / .ThenBy(m => m.Name.ToUpper().IndexOf(term)) // the earlier our term appears in the name, the more likely it is to be relevant (particularly with substring matches) .ThenByDescending(m => m.ApprovedTime) .ThenBy(m => m.Name); //Lots of people are trying to search only with one or two words that really should just be tag searches //If there's no tags passed, this try to do a tag search in addition to the text-based search if (request.Tags.Count() == 0) { tagSearch = tagSearch .Where(m => m.MiniTags .Where(x => x.Status == Status.Approved) .Select(x => x.Tag) .Any(t => t.TagName == term) ); } } if (request.Tags.Count() == 0) { search = tagSearch.Union(search); } } //TODO: Just hacking this in for now. We're double sorting... if (request.SortType == "newest") { search = search.OrderByDescending(m => m.ID); } return(await PaginatedList.CreateAsync(search, request.PageInfo)); }