public async Task <SearchBlogResult> SearchBlogAsync(SearchModel m, int pageNumber, int pageSize) { SearchBlogResult result = new SearchBlogResult { SearchModel = m }; var predicate = PredicateBuilder.New <Blog>(true); if (!string.IsNullOrWhiteSpace(m.FavUser)) { predicate = predicate.And(b => _db.Favorites.Where(f => f.Username == m.FavUser).Any(f => f.BlogID == b.BlogID)); } // If searching by title, include isApproved == null if (!string.IsNullOrWhiteSpace(m.Title)) { predicate = predicate.And(b => b.isApproved != false); } else { predicate = predicate.And(b => b.isApproved == true); } if (m.StartDate.HasValue) { predicate = predicate.And(b => b.BlogDate >= m.StartDate.Value); } if (m.EndDate.HasValue) { var enddate = new DateTime(m.EndDate.Value.Year, m.EndDate.Value.Month, m.EndDate.Value.Day, 23, 59, 59); predicate = predicate.And(b => b.BlogDate <= enddate); } if (!string.IsNullOrWhiteSpace(m.Author)) { predicate = predicate.And(b => b.Author == m.Author); } List <int> flatCategories; if (m.CurrentCategory.HasValue) { flatCategories = _categoryUtil.GetCategoryWithSubcategories(m.CurrentCategory.Value); predicate = predicate.And(b => flatCategories.Contains(b.CategoryID)); } if (m.CategoryIds != null && m.CategoryIds.Count() > 0) { flatCategories = m.CategoryIds.Aggregate(new List <int>(), (l, id) => { l.AddRange(_categoryUtil.GetCategoryWithSubcategories(id)); return(l); }); predicate = predicate.And(b => flatCategories.Contains(b.CategoryID)); } if (!string.IsNullOrWhiteSpace(m.Tags)) { var tags = TagUtil.SplitTags(m.Tags); IQueryable <TagsInBlog> tagsInBlog; if (!m.TagsMatchAny) { tagsInBlog = tags.Aggregate(_db.TagsInBlogs.AsExpandable(), (r, name) => r.Join( _db.TagsInBlogs.Where(tt => tt.tag.TagName.ToLower().Contains(name.ToLower())), rr => rr.BlogID, t => t.BlogID, (rr, t) => rr)); } else { tagsInBlog = tags.Aggregate(_db.TagsInBlogs.AsExpandable().Where(_ => false), (r, name) => r.Union( _db.TagsInBlogs.AsExpandable().Where(tt => tt.tag.TagName.ToLower().Contains(name.ToLower()))).Distinct()); } var tagResult = await tagsInBlog.Select(tib => new { blogid = tib.BlogID, tag = tib.tag }).Distinct().ToListAsync(); if (!m.TagsMatchAny) { result.TagsSearched = tagResult.Where(tib => tags.Any(name => tib.tag.TagName.ToSingleByteCharacterString().ToLower().Contains( name.ToSingleByteCharacterString().ToLower()))) .Select(tib => tib.tag).Distinct(); } else { result.TagsSearched = tagResult.Select(tib => tib.tag).Distinct(); } var blogIds = tagResult.Select(tib => tib.blogid).Distinct(); predicate = predicate.And(b => blogIds.Contains(b.BlogID)); } if (!string.IsNullOrWhiteSpace(m.Title)) { var keywords = m.Title.Replace('(', ' ').Replace(')', ' ').Replace('"', ' ').Split(new char[0], StringSplitOptions.RemoveEmptyEntries); var search = _db.ContainsSearchBlog(string.Join(m.TitleMatchAny ? " OR " : " AND ", keywords.Select(s => '"' + s + '"'))); var TitlePredicate = PredicateBuilder.New <Blog>(true); if (!m.TitleMatchAny) { TitlePredicate = TitlePredicate.And(keywords.Aggregate(PredicateBuilder.New <Blog>(true), (p, word) => p.And(b => b.BlogTitle.Contains(word)))); } else { TitlePredicate = TitlePredicate.And(keywords.Aggregate(PredicateBuilder.New <Blog>(false), (p, word) => p.Or(b => b.BlogTitle.Contains(word)))); } TitlePredicate = TitlePredicate.Or(b => search.Count(r => r.BlogID == b.BlogID) > 0); predicate = predicate.And(TitlePredicate); } if (m.Harmony.HasValue) { predicate = predicate.And(b => b.isHarmony == m.Harmony.Value); } result.Blogs = await BlogHelper.getSortedQuery(_db, _db.Blogs.AsExpandable().Where(predicate), m.Sort).ToPagedListAsync(pageNumber, pageSize); return(result); }
public async Task <SearchBlogResult> SearchBlogAsync(SearchModel m, int pageNumber, int pageSize) { SearchBlogResult searchBlogResult = new SearchBlogResult { SearchModel = m }; if (!IsValid()) { // Fallback return(await _dbBlogSearch.SearchBlogAsync(m, pageNumber, pageSize)); } double?minScore = null; QueryContainer query(QueryContainerDescriptor <BlogIndexed> q) { var queries = new List <QueryContainer>(); if (!string.IsNullOrWhiteSpace(m.Title)) { queries.Add(q.Bool(b => b.MustNot(q.Term("isApproved", false)))); } else { queries.Add(q.Term("isApproved", true)); } if (m.StartDate.HasValue) { queries.Add(q.DateRange(dr => dr.Field(f => f.CreateDate).GreaterThanOrEquals(m.StartDate))); } if (m.EndDate.HasValue) { var enddate = new DateTime(m.EndDate.Value.Year, m.EndDate.Value.Month, m.EndDate.Value.Day, 23, 59, 59); queries.Add(q.DateRange(dr => dr.Field(f => f.CreateDate).LessThanOrEquals(enddate))); } if (!string.IsNullOrWhiteSpace(m.Author)) { queries.Add(q.Term("author", m.Author)); } IEnumerable <int> flatCategories = null; if (m.CurrentCategory.HasValue) { flatCategories = _categoryUtil.GetCategoryWithSubcategories(m.CurrentCategory.Value); } if (m.CategoryIds != null && m.CategoryIds.Count() > 0) { var ids = m.CategoryIds.Aggregate(new List <int>(), (l, id) => { l.AddRange(_categoryUtil.GetCategoryWithSubcategories(id)); return(l); }); if (flatCategories != null) { flatCategories = flatCategories.Intersect(ids); } else { flatCategories = ids; } } if (flatCategories != null && flatCategories.Count() > 0) { queries.Add(q.Terms(ts => ts.Field("categoryId").Terms(flatCategories))); } if (!string.IsNullOrWhiteSpace(m.Tags)) { var tags = TagUtil.SplitTags(m.Tags); var tagQueries = tags.Select(t => q.Bool(b => b.Should( q.Match(mm => mm.Field("tags").Query(t).Operator(Operator.And)), q.Match(mm => mm.Field("tags.ngram_lc").Query(t).Operator(Operator.And))))).ToArray(); if (m.TagsMatchAny) { queries.Add(q.Bool(b => b.Should(tagQueries))); } else { queries.Add(q.Bool(b => b.Must(tagQueries))); } } if (!string.IsNullOrWhiteSpace(m.Title)) { var titles = m.TitleMatchAny ? m.Title.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) : new[] { m.Title }; var field = m.Title.Length > 30 ? "title" : "title.ngram_lc"; queries.Add(q.Bool(b => b.Should( titles.Select(t => new QueryContainer(new MatchQuery { Field = field, Query = t, Operator = Operator.And })).ToArray()))); minScore = 5; } if (!string.IsNullOrWhiteSpace(m.Query)) { queries.Add(q.MultiMatch(mm => mm.Query(m.Query).Operator(Operator.And).Fields(new[] { "tags", "title", "title.ngram_lc", "content" }))); minScore = 5; } if (m.Harmony == true) { queries.Add(q.Term("isHarmony", true)); } return(q.Bool(bs => bs.Must(queries.ToArray()))); } SortDescriptor <BlogIndexed> selector(SortDescriptor <BlogIndexed> s) { switch (m.Sort) { case "Date": return(s.Ascending(b => b.CreateDate)); case "Date_desc": return(s.Descending(b => b.CreateDate)); case "Visit_desc": return(s.Descending(q => q.BlogVisit)); case "Visit": return(s.Ascending(q => q.BlogVisit)); case "Post": return(s.Ascending(q => q.PostCount)); case "Post_desc": return(s.Descending(q => q.PostCount)); case "Rate": return(s.Ascending(q => q.Rating)); case "Rate_desc": return(s.Descending(q => q.Rating)); case "Score": return(s.Ascending(SortSpecialField.Score)); case "Score_desc": return(s.Descending(SortSpecialField.Score)); // TODO: AddDate, AddDate_desc for search in favorite. case null: default: if (string.IsNullOrWhiteSpace(m.Query)) { return(s.Descending(b => b.CreateDate)); } return(s.Descending(SortSpecialField.Score)); } } var result = await _client.SearchAsync <BlogIndexed>(s => { s = s.Query(query) .TrackTotalHits(true) .MinScore(minScore) .Size(pageSize) .Skip((pageNumber - 1) * pageSize) .Sort(selector); if (new[] { m.Tags, m.Title, m.Query }.Any(v => !string.IsNullOrWhiteSpace(v))) { s = s.Aggregations(agg => agg .Terms("distinct_tags", tg => tg.Field("tags.kw").Size(10)) .Terms("categories", tg => tg.Field(b => b.CategoryId).Size(_categoryUtil.GetCategoryList().Count))); } return(s); }, _httpContext.RequestAborted); if (result.IsValid) { searchBlogResult.Blogs = new X.PagedList.StaticPagedList <Blog>(result.Documents.Select(d => d.ToBlog()), pageNumber, pageSize, (int)result.Total); if (result.Aggregations.ContainsKey("distinct_tags")) { var tags = result.Aggregations.Terms("distinct_tags").Buckets.Select(b => b.Key); searchBlogResult.TagsSearched = await _db.Tags.Where(t => tags.Contains(t.TagName)).ToListAsync(); } if (result.Aggregations.ContainsKey("categories")) { var counts = result.Aggregations.Terms <int>("categories").Buckets.ToDictionary(k => k.Key, k => k.DocCount.GetValueOrDefault(0)); long CalculateTotalItems(Category c) { if (c == null) { return(0); } counts.TryGetValue(c.CategoryID, out long count); if (c.SubCategories != null) { foreach (var subcat in c.SubCategories) { count += CalculateTotalItems(subcat); } counts[c.CategoryID] = count; } return(count); } long total = 0; foreach (var main in _categoryUtil.GetCategoryList().Where(h => !h.ParentCategoryID.HasValue)) { total += CalculateTotalItems(main); } counts[0] = total; searchBlogResult.SearchModel.CategoryItemCount = counts.Where(v => v.Value > 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } } else { _logger.LogError(result.DebugInformation); searchBlogResult.Blogs = new X.PagedList.PagedList <Blog>(Enumerable.Empty <Blog>(), pageNumber, pageSize); } return(searchBlogResult); }