/// <summary>
        /// Bypasses the index provider and directly searches in the database.
        /// </summary>
        /// <param name="searchQuery">Search query.</param>
        /// <returns>Forum search result.</returns>
        protected virtual ForumSearchResult SearchDirect(ForumSearchQuery searchQuery)
        {
            // Fallback to linq search.
            var linqForumSearchService = _services.Container.ResolveNamed <IForumSearchService>("linq");
            var result = linqForumSearchService.Search(searchQuery, true);

            return(result);
        }
Beispiel #2
0
        public ForumSearchResult Search(ForumSearchQuery searchQuery, bool direct = false)
        {
            _services.EventPublisher.Publish(new ForumSearchingEvent(searchQuery));

            var totalHits = 0;

            int[] hitsEntityIds = null;
            Func <IList <ForumPost> >        hitsFactory = null;
            IDictionary <string, FacetGroup> facets      = null;

            if (searchQuery.Take > 0)
            {
                var query = GetPostQuery(searchQuery, null);
                totalHits = query.Count();

                // Fix paging boundaries.
                if (searchQuery.Skip > 0 && searchQuery.Skip >= totalHits)
                {
                    searchQuery.Slice((totalHits / searchQuery.Take) * searchQuery.Take, searchQuery.Take);
                }

                if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits))
                {
                    var skip = searchQuery.PageIndex * searchQuery.Take;
                    query = query
                            .Skip(() => skip)
                            .Take(() => searchQuery.Take);

                    hitsEntityIds = query.Select(x => x.Id).ToArray();
                    hitsFactory   = () => _forumService.GetPostsByIds(hitsEntityIds);
                }

                if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithFacets) && searchQuery.FacetDescriptors.Any())
                {
                    facets = GetFacets(searchQuery, totalHits);
                }
            }

            var result = new ForumSearchResult(
                null,
                searchQuery,
                totalHits,
                hitsEntityIds,
                hitsFactory,
                null,
                facets);

            _services.EventPublisher.Publish(new ForumSearchedEvent(searchQuery, result));

            return(result);
        }
Beispiel #3
0
        public ForumSearchResult(
            ISearchEngine engine,
            ForumSearchQuery query,
            int totalHitsCount,
            Func <IList <ForumPost> > hitsFactory,
            string[] spellCheckerSuggestions,
            IDictionary <string, FacetGroup> facets)
        {
            Guard.NotNull(query, nameof(query));

            Engine = engine;
            Query  = query;
            SpellCheckerSuggestions = spellCheckerSuggestions ?? new string[0];
            Facets = facets ?? new Dictionary <string, FacetGroup>();

            _hitsFactory   = hitsFactory ?? (() => new List <ForumPost>());
            TotalHitsCount = totalHitsCount;
        }
Beispiel #4
0
 /// <summary>
 /// Constructor for an instance without any search hits
 /// </summary>
 /// <param name="query">Forum search query</param>
 public ForumSearchResult(ForumSearchQuery query)
     : this(null, query, 0, null, () => new List <ForumPost>(), null, null)
 {
 }
Beispiel #5
0
        protected virtual IQueryable <ForumPost> GetPostQuery(ForumSearchQuery searchQuery, IQueryable <ForumPost> baseQuery)
        {
            // Post query.
            var ordered  = false;
            var t        = searchQuery.Term;
            var cnf      = _customerSettings.CustomerNameFormat;
            var fields   = searchQuery.Fields;
            var filters  = new List <ISearchFilter>();
            var customer = _services.WorkContext.CurrentCustomer;
            var query    = baseQuery ?? _forumPostRepository.TableUntracked.Expand(x => x.ForumTopic);

            // Apply search term.
            if (t.HasValue() && fields != null && fields.Length != 0 && fields.Any(x => x.HasValue()))
            {
                if (searchQuery.Mode == SearchMode.StartsWith)
                {
                    query = query.Where(x =>
                                        (fields.Contains("subject") && x.ForumTopic.Subject.StartsWith(t)) ||
                                        (fields.Contains("text") && x.Text.StartsWith(t)) ||
                                        (fields.Contains("username") && (
                                             cnf == CustomerNameFormat.ShowEmails ? x.Customer.Email.StartsWith(t) :
                                             cnf == CustomerNameFormat.ShowUsernames ? x.Customer.Username.StartsWith(t) :
                                             cnf == CustomerNameFormat.ShowFirstName ? x.Customer.FirstName.StartsWith(t) :
                                             x.Customer.FullName.StartsWith(t))
                                        ));
                }
                else
                {
                    query = query.Where(x =>
                                        (fields.Contains("subject") && x.ForumTopic.Subject.Contains(t)) ||
                                        (fields.Contains("text") && x.Text.Contains(t)) ||
                                        (fields.Contains("username") && (
                                             cnf == CustomerNameFormat.ShowEmails ? x.Customer.Email.Contains(t) :
                                             cnf == CustomerNameFormat.ShowUsernames ? x.Customer.Username.Contains(t) :
                                             cnf == CustomerNameFormat.ShowFirstName ? x.Customer.FirstName.Contains(t) :
                                             x.Customer.FullName.Contains(t))
                                        ));
                }
            }

            // Flatten filters.
            foreach (var filter in searchQuery.Filters)
            {
                var combinedFilter = filter as ICombinedSearchFilter;
                if (combinedFilter != null)
                {
                    // Find VisibleOnly combined filter and process it separately.
                    var cf = combinedFilter.Filters.OfType <IAttributeSearchFilter>().ToArray();
                    if (cf.Length == 2 && cf[0].FieldName == "published" && true == (bool)cf[0].Term && cf[1].FieldName == "customerid")
                    {
                        if (!customer.IsForumModerator())
                        {
                            query = query.Where(x => x.ForumTopic.Published && (x.Published || x.CustomerId == customer.Id));
                        }
                    }
                    else
                    {
                        FlattenFilters(combinedFilter.Filters, filters);
                    }
                }
                else
                {
                    filters.Add(filter);
                }
            }

            if (!QuerySettings.IgnoreAcl)
            {
                var roleIds = GetIdList(filters, "roleid");
                if (roleIds.Any())
                {
                    query =
                        from fp in query
                        join ft in _forumTopicRepository.TableUntracked on fp.TopicId equals ft.Id
                        join ff in _forumRepository.Table on ft.ForumId equals ff.Id
                        join fg in _forumGroupRepository.Table on ff.ForumGroupId equals fg.Id
                        join a in _aclRepository.Table on new { a1 = fg.Id, a2 = "ForumGroup" } equals new { a1 = a.EntityId, a2 = a.EntityName } into fg_acl
                    from a in fg_acl.DefaultIfEmpty()
                    where !fg.SubjectToAcl || roleIds.Contains(a.CustomerRoleId)
                    select fp;
                }
            }

            foreach (IAttributeSearchFilter filter in filters)
            {
                var rangeFilter = filter as IRangeSearchFilter;

                if (filter.FieldName == "id")
                {
                    if (rangeFilter != null)
                    {
                        var lower = filter.Term as int?;
                        var upper = rangeFilter.UpperTerm as int?;

                        if (lower.HasValue)
                        {
                            if (rangeFilter.IncludesLower)
                            {
                                query = query.Where(x => x.Id >= lower.Value);
                            }
                            else
                            {
                                query = query.Where(x => x.Id > lower.Value);
                            }
                        }

                        if (upper.HasValue)
                        {
                            if (rangeFilter.IncludesUpper)
                            {
                                query = query.Where(x => x.Id <= upper.Value);
                            }
                            else
                            {
                                query = query.Where(x => x.Id < upper.Value);
                            }
                        }
                    }
                }
                else if (filter.FieldName == "forumid")
                {
                    query = query.Where(x => x.ForumTopic.ForumId == (int)filter.Term);
                }
                else if (filter.FieldName == "customerid")
                {
                    query = query.Where(x => x.CustomerId == (int)filter.Term);
                }
                else if (filter.FieldName == "published")
                {
                    query = query.Where(x => x.Published == (bool)filter.Term);
                }
                else if (filter.FieldName == "createdon")
                {
                    if (rangeFilter != null)
                    {
                        var lower = filter.Term as DateTime?;
                        var upper = rangeFilter.UpperTerm as DateTime?;

                        if (lower.HasValue)
                        {
                            if (rangeFilter.IncludesLower)
                            {
                                query = query.Where(x => x.CreatedOnUtc >= lower.Value);
                            }
                            else
                            {
                                query = query.Where(x => x.CreatedOnUtc > lower.Value);
                            }
                        }

                        if (upper.HasValue)
                        {
                            if (rangeFilter.IncludesLower)
                            {
                                query = query.Where(x => x.CreatedOnUtc <= upper.Value);
                            }
                            else
                            {
                                query = query.Where(x => x.CreatedOnUtc < upper.Value);
                            }
                        }
                    }
                }
                else if (filter.FieldName == "storeid")
                {
                    if (!QuerySettings.IgnoreMultiStore)
                    {
                        var storeId = (int)filter.Term;
                        if (storeId != 0)
                        {
                            query =
                                from p in query
                                join sm in _storeMappingRepository.TableUntracked on new { eid = p.ForumTopic.Forum.ForumGroupId, ename = "ForumGroup" }
                            equals new { eid = sm.EntityId, ename = sm.EntityName } into gsm
                            from sm in gsm.DefaultIfEmpty()
                            where !p.ForumTopic.Forum.ForumGroup.LimitedToStores || sm.StoreId == storeId
                            select p;
                        }
                    }
                }
            }

            query =
                from p in query
                group p by p.Id into grp
                orderby grp.Key
                select grp.FirstOrDefault();

            // Sorting.
            foreach (var sort in searchQuery.Sorting)
            {
                if (sort.FieldName == "subject")
                {
                    query = OrderBy(ref ordered, query, x => x.ForumTopic.Subject, sort.Descending);
                }
                else if (sort.FieldName == "username")
                {
                    switch (cnf)
                    {
                    case CustomerNameFormat.ShowEmails:
                        query = OrderBy(ref ordered, query, x => x.Customer.Email, sort.Descending);
                        break;

                    case CustomerNameFormat.ShowUsernames:
                        query = OrderBy(ref ordered, query, x => x.Customer.Username, sort.Descending);
                        break;

                    case CustomerNameFormat.ShowFirstName:
                        query = OrderBy(ref ordered, query, x => x.Customer.FirstName, sort.Descending);
                        break;

                    default:
                        query = OrderBy(ref ordered, query, x => x.Customer.FullName, sort.Descending);
                        break;
                    }
                }
                else if (sort.FieldName == "createdon")
                {
                    // We want to sort by ForumPost.CreatedOnUtc, not ForumTopic.CreatedOnUtc.
                    query = OrderBy(ref ordered, query, x => x.ForumTopic.LastPostTime, sort.Descending);
                }
                else if (sort.FieldName == "numposts")
                {
                    query = OrderBy(ref ordered, query, x => x.ForumTopic.NumPosts, sort.Descending);
                }
            }

            if (!ordered)
            {
                query = query
                        .OrderByDescending(x => x.ForumTopic.TopicTypeId)
                        .ThenByDescending(x => x.ForumTopic.LastPostTime)
                        .ThenByDescending(x => x.TopicId);
            }

            return(query);
        }
Beispiel #6
0
 public IQueryable <ForumPost> PrepareQuery(ForumSearchQuery searchQuery, IQueryable <ForumPost> baseQuery = null)
 {
     return(GetPostQuery(searchQuery, baseQuery));
 }
Beispiel #7
0
        protected virtual IDictionary <string, FacetGroup> GetFacets(ForumSearchQuery searchQuery, int totalHits)
        {
            var result     = new Dictionary <string, FacetGroup>();
            var storeId    = searchQuery.StoreId ?? _services.StoreContext.CurrentStore.Id;
            var languageId = searchQuery.LanguageId ?? _services.WorkContext.WorkingLanguage.Id;

            foreach (var key in searchQuery.FacetDescriptors.Keys)
            {
                var descriptor = searchQuery.FacetDescriptors[key];
                var facets     = new List <Facet>();
                var kind       = FacetGroup.GetKindByKey("Forum", key);

                if (kind == FacetGroupKind.Forum)
                {
                    var enoughFacets = false;
                    var groups       = _forumService.GetAllForumGroups(storeId);

                    foreach (var group in groups)
                    {
                        foreach (var forum in group.Forums)
                        {
                            facets.Add(new Facet(new FacetValue(forum.Id, IndexTypeCode.Int32)
                            {
                                IsSelected   = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(forum.Id)),
                                Label        = forum.GetLocalized(x => x.Name, languageId),
                                DisplayOrder = forum.DisplayOrder
                            }));

                            if (descriptor.MaxChoicesCount > 0 && facets.Count >= descriptor.MaxChoicesCount)
                            {
                                enoughFacets = true;
                                break;
                            }
                        }

                        if (enoughFacets)
                        {
                            break;
                        }
                    }
                }
                else if (kind == FacetGroupKind.Customer)
                {
                    // Get customers with most posts.
                    var customerQuery = FacetUtility.GetCustomersByNumberOfPosts(
                        _forumPostRepository,
                        _storeMappingRepository,
                        QuerySettings.IgnoreMultiStore ? 0 : storeId,
                        descriptor.MinHitCount);

                    // Limit the result. Do not allow to get all customers.
                    var maxChoices = descriptor.MaxChoicesCount > 0 ? descriptor.MaxChoicesCount : 20;
                    var take       = maxChoices * 3;
                    var customers  = customerQuery.Take(() => take).ToList();

                    foreach (var customer in customers)
                    {
                        var name = customer.FormatUserName(_customerSettings, T, true);
                        if (name.HasValue())
                        {
                            facets.Add(new Facet(new FacetValue(customer.Id, IndexTypeCode.Int32)
                            {
                                IsSelected   = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(customer.Id)),
                                Label        = name,
                                DisplayOrder = 0
                            }));
                            if (facets.Count >= maxChoices)
                            {
                                break;
                            }
                        }
                    }
                }
                else if (kind == FacetGroupKind.Date)
                {
                    foreach (var value in descriptor.Values)
                    {
                        facets.Add(new Facet(value));
                    }
                }

                if (facets.Any(x => x.Published))
                {
                    //facets.Each(x => $"{key} {x.Value.ToString()}".Dump());

                    var group = new FacetGroup(
                        "Forum",
                        key,
                        descriptor.Label,
                        descriptor.IsMultiSelect,
                        false,
                        descriptor.DisplayOrder,
                        facets.OrderBy(descriptor))
                    {
                        IsScrollable = facets.Count > 14
                    };

                    result.Add(key, group);
                }
            }

            return(result);
        }
        public ForumSearchResult Search(ForumSearchQuery searchQuery, bool direct = false)
        {
            Guard.NotNull(searchQuery, nameof(searchQuery));
            Guard.NotNegative(searchQuery.Take, nameof(searchQuery.Take));

            var provider = _indexManager.GetIndexProvider("Forum");

            if (!direct && provider != null)
            {
                var indexStore = provider.GetIndexStore("Forum");
                if (indexStore.Exists)
                {
                    var      searchEngine            = provider.GetSearchEngine(indexStore, searchQuery);
                    var      stepPrefix              = searchEngine.GetType().Name + " - ";
                    var      totalCount              = 0;
                    int[]    hitsEntityIds           = null;
                    string[] spellCheckerSuggestions = null;
                    IEnumerable <ISearchHit>         searchHits;
                    Func <IList <ForumPost> >        hitsFactory = null;
                    IDictionary <string, FacetGroup> facets      = null;

                    _services.EventPublisher.Publish(new ForumSearchingEvent(searchQuery));

                    if (searchQuery.Take > 0)
                    {
                        using (_services.Chronometer.Step(stepPrefix + "Count"))
                        {
                            totalCount = searchEngine.Count();
                            // Fix paging boundaries.
                            if (searchQuery.Skip > 0 && searchQuery.Skip >= totalCount)
                            {
                                searchQuery.Slice((totalCount / searchQuery.Take) * searchQuery.Take, searchQuery.Take);
                            }
                        }

                        using (_services.Chronometer.Step(stepPrefix + "Hits"))
                        {
                            searchHits = searchEngine.Search();
                        }

                        if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits))
                        {
                            using (_services.Chronometer.Step(stepPrefix + "Collect"))
                            {
                                hitsEntityIds = searchHits.Select(x => x.EntityId).ToArray();
                                hitsFactory   = () => _forumService.Value.GetPostsByIds(hitsEntityIds);
                            }
                        }

                        if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithFacets))
                        {
                            try
                            {
                                using (_services.Chronometer.Step(stepPrefix + "Facets"))
                                {
                                    facets = searchEngine.GetFacetMap();
                                }
                            }
                            catch (Exception ex)
                            {
                                _logger.Error(ex);
                            }
                        }
                    }

                    if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithSuggestions))
                    {
                        try
                        {
                            using (_services.Chronometer.Step(stepPrefix + "Spellcheck"))
                            {
                                spellCheckerSuggestions = searchEngine.CheckSpelling();
                            }
                        }
                        catch (Exception ex)
                        {
                            // Spell checking should not break the search.
                            _logger.Error(ex);
                        }
                    }

                    var result = new ForumSearchResult(
                        searchEngine,
                        searchQuery,
                        totalCount,
                        hitsEntityIds,
                        hitsFactory,
                        spellCheckerSuggestions,
                        facets);

                    _services.EventPublisher.Publish(new ForumSearchedEvent(searchQuery, result));

                    return(result);
                }
                else if (searchQuery.Origin.IsCaseInsensitiveEqual("Boards/Search"))
                {
                    IndexingRequiredNotification(_services, _urlHelper);
                }
            }

            return(SearchDirect(searchQuery));
        }
        public IQueryable <ForumPost> PrepareQuery(ForumSearchQuery searchQuery, IQueryable <ForumPost> baseQuery = null)
        {
            var linqForumSearchService = _services.Container.ResolveNamed <IForumSearchService>("linq");

            return(linqForumSearchService.PrepareQuery(searchQuery, baseQuery));
        }
        protected virtual IQueryable <ForumPost> GetPostQuery(ForumSearchQuery searchQuery, IQueryable <ForumPost> baseQuery)
        {
            // Post query.
            var ordered = false;
            var t       = searchQuery.Term;
            var cnf     = _customerSettings.CustomerNameFormat;
            var fields  = searchQuery.Fields;
            var filters = new List <ISearchFilter>();
            var query   = baseQuery ?? _forumPostRepository.TableUntracked.Expand(x => x.ForumTopic);

            // Apply search term.
            if (t.HasValue() && fields != null && fields.Length != 0 && fields.Any(x => x.HasValue()))
            {
                if (searchQuery.Mode == SearchMode.StartsWith)
                {
                    query = query.Where(x =>
                                        (fields.Contains("subject") && x.ForumTopic.Subject.StartsWith(t)) ||
                                        (fields.Contains("text") && x.Text.StartsWith(t)) ||
                                        (fields.Contains("username") && (
                                             cnf == CustomerNameFormat.ShowEmails ? x.Customer.Email.StartsWith(t) :
                                             cnf == CustomerNameFormat.ShowUsernames ? x.Customer.Username.StartsWith(t) :
                                             cnf == CustomerNameFormat.ShowFirstName ? x.Customer.FirstName.StartsWith(t) :
                                             x.Customer.FullName.StartsWith(t))
                                        ));
                }
                else
                {
                    query = query.Where(x =>
                                        (fields.Contains("subject") && x.ForumTopic.Subject.Contains(t)) ||
                                        (fields.Contains("text") && x.Text.Contains(t)) ||
                                        (fields.Contains("username") && (
                                             cnf == CustomerNameFormat.ShowEmails ? x.Customer.Email.Contains(t) :
                                             cnf == CustomerNameFormat.ShowUsernames ? x.Customer.Username.Contains(t) :
                                             cnf == CustomerNameFormat.ShowFirstName ? x.Customer.FirstName.Contains(t) :
                                             x.Customer.FullName.Contains(t))
                                        ));
                }
            }

            // Filters.
            FlattenFilters(searchQuery.Filters, filters);

            foreach (IAttributeSearchFilter filter in filters)
            {
                var rangeFilter = filter as IRangeSearchFilter;

                if (filter.FieldName == "id")
                {
                    if (rangeFilter != null)
                    {
                        var lower = filter.Term as int?;
                        var upper = rangeFilter.UpperTerm as int?;

                        if (lower.HasValue)
                        {
                            if (rangeFilter.IncludesLower)
                            {
                                query = query.Where(x => x.Id >= lower.Value);
                            }
                            else
                            {
                                query = query.Where(x => x.Id > lower.Value);
                            }
                        }

                        if (upper.HasValue)
                        {
                            if (rangeFilter.IncludesUpper)
                            {
                                query = query.Where(x => x.Id <= upper.Value);
                            }
                            else
                            {
                                query = query.Where(x => x.Id < upper.Value);
                            }
                        }
                    }
                }
                else if (filter.FieldName == "forumid")
                {
                    query = query.Where(x => x.ForumTopic.ForumId == (int)filter.Term);
                }
                else if (filter.FieldName == "customerid")
                {
                    query = query.Where(x => x.CustomerId == (int)filter.Term);
                }
                else if (filter.FieldName == "createdon")
                {
                    if (rangeFilter != null)
                    {
                        var lower = filter.Term as DateTime?;
                        var upper = rangeFilter.UpperTerm as DateTime?;

                        if (lower.HasValue)
                        {
                            if (rangeFilter.IncludesLower)
                            {
                                query = query.Where(x => x.CreatedOnUtc >= lower.Value);
                            }
                            else
                            {
                                query = query.Where(x => x.CreatedOnUtc > lower.Value);
                            }
                        }

                        if (upper.HasValue)
                        {
                            if (rangeFilter.IncludesLower)
                            {
                                query = query.Where(x => x.CreatedOnUtc <= upper.Value);
                            }
                            else
                            {
                                query = query.Where(x => x.CreatedOnUtc < upper.Value);
                            }
                        }
                    }
                }
                else if (filter.FieldName == "storeid")
                {
                    if (!QuerySettings.IgnoreMultiStore)
                    {
                        var storeId = (int)filter.Term;
                        if (storeId != 0)
                        {
                            query =
                                from p in query
                                join sm in _storeMappingRepository.TableUntracked on new { eid = p.ForumTopic.Forum.ForumGroupId, ename = "ForumGroup" }
                            equals new { eid = sm.EntityId, ename = sm.EntityName } into gsm
                            from sm in gsm.DefaultIfEmpty()
                            where !p.ForumTopic.Forum.ForumGroup.LimitedToStores || sm.StoreId == storeId
                            select p;
                        }
                    }
                }
            }

            query =
                from p in query
                group p by p.Id into grp
                orderby grp.Key
                select grp.FirstOrDefault();

            // Sorting.
            foreach (var sort in searchQuery.Sorting)
            {
                if (sort.FieldName == "subject")
                {
                    query = OrderBy(ref ordered, query, x => x.ForumTopic.Subject, sort.Descending);
                }
                else if (sort.FieldName == "username")
                {
                    switch (cnf)
                    {
                    case CustomerNameFormat.ShowEmails:
                        query = OrderBy(ref ordered, query, x => x.Customer.Email, sort.Descending);
                        break;

                    case CustomerNameFormat.ShowUsernames:
                        query = OrderBy(ref ordered, query, x => x.Customer.Username, sort.Descending);
                        break;

                    case CustomerNameFormat.ShowFirstName:
                        query = OrderBy(ref ordered, query, x => x.Customer.FirstName, sort.Descending);
                        break;

                    default:
                        query = OrderBy(ref ordered, query, x => x.Customer.FullName, sort.Descending);
                        break;
                    }
                }
                else if (sort.FieldName == "createdon")
                {
                    // We want to sort by ForumPost.CreatedOnUtc, not ForumTopic.CreatedOnUtc.
                    query = OrderBy(ref ordered, query, x => x.ForumTopic.LastPostTime, sort.Descending);
                }
                else if (sort.FieldName == "numposts")
                {
                    query = OrderBy(ref ordered, query, x => x.ForumTopic.NumPosts, sort.Descending);
                }
            }

            if (!ordered)
            {
                query = query
                        .OrderByDescending(x => x.ForumTopic.TopicTypeId)
                        .ThenByDescending(x => x.ForumTopic.LastPostTime)
                        .ThenByDescending(x => x.TopicId);
            }

            return(query);
        }