/// <summary> /// Adds language restriction to search query, if possible /// </summary> /// <param name="query">Entity query</param> /// <param name="booleanQuery">Search query</param> private void AddLanguageRestrictionToQuery(ICrmEntityQuery query, BooleanQuery booleanQuery) { if (string.IsNullOrEmpty(this.Index.LanguageLocaleCodeFieldName)) { return; } // Multilanguage enabled, language code provided in query if (query.MultiLanguageEnabled && !string.IsNullOrEmpty(query.ContextLanguage.Code)) { var languageQuery = new BooleanQuery(); languageQuery.Add(new TermQuery(new Term(this.Index.LanguageLocaleCodeFieldName, query.ContextLanguage.Code.ToLowerInvariant())), Occur.SHOULD); languageQuery.Add(new TermQuery(new Term(this.Index.LanguageLocaleCodeFieldName, this.Index.LanguageLocaleCodeDefaultValue)), Occur.SHOULD); booleanQuery.Add(languageQuery, Occur.MUST); } //// Multilanguage disabled, KB language present in settings else if (!query.MultiLanguageEnabled && !string.IsNullOrWhiteSpace(this.Index.LanguageLocaleCode)) { var languageQuery = new BooleanQuery(); languageQuery.Add(new TermQuery(new Term(this.Index.LanguageLocaleCodeFieldName, this.Index.LanguageLocaleCode)), Occur.SHOULD); languageQuery.Add(new TermQuery(new Term(this.Index.LanguageLocaleCodeFieldName, this.Index.LanguageLocaleCodeDefaultValue)), Occur.SHOULD); booleanQuery.Add(languageQuery, Occur.MUST); } //// Otherwise, do not restrict the language }
protected override Query CreateQuery(ICrmEntityQuery query) { var scopedQuery = query as IScopedEntityQuery; if (scopedQuery == null) { return(base.CreateQuery(query)); } var scopeQuery = new BooleanQuery(); foreach (var scope in scopedQuery.Scopes) { if (scope == null) { continue; } scopeQuery.Add(new TermQuery(new Term(Index.ScopeFieldName, scope)), Occur.SHOULD); } scopeQuery.Add(new TermQuery(new Term(Index.ScopeFieldName, Index.ScopeDefaultValue)), Occur.SHOULD); var baseQuery = base.CreateQuery(scopedQuery); var compositeQuery = new BooleanQuery { { baseQuery, Occur.MUST }, { scopeQuery, Occur.MUST } }; return(compositeQuery); }
/// <summary> /// Gets the unprocessed results for a search query. /// </summary> /// <param name="query">The query</param> /// <param name="searchLimit">The max number of results we want</param> /// <param name="offset">The number of already processed results to skip.</param> /// <returns>The results of the search unfiltered for the user.</returns> protected virtual RawSearchResultSet GetRawSearchResults(ICrmEntityQuery query, int searchLimit, int rawOffset) { var stopwatch = new Stopwatch(); stopwatch.Start(); var luceneQuery = CreateQuery(query); TopDocs topDocs; try { topDocs = _searcher.Search(luceneQuery, searchLimit); } catch (Exception e) { SearchEventSource.Log.QueryError(e); throw; } stopwatch.Stop(); ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("Lucene: {0} total hits ({1}ms)", topDocs.TotalHits, stopwatch.ElapsedMilliseconds)); PortalFeatureTrace.TraceInstance.LogSearch(FeatureTraceCategory.Search, topDocs.TotalHits, stopwatch.ElapsedMilliseconds, string.Format("Lucene: {0} total hits ({1}ms)", topDocs.TotalHits, stopwatch.ElapsedMilliseconds)); return(ConvertTopDocsToRawSearchResultSet(topDocs, rawOffset)); }
protected virtual Query CreateQuery(ICrmEntityQuery query) { var booleanQuery = new BooleanQuery(); var queryParser = new QueryParser(Index.Version, Index.ContentFieldName, Index.GetQuerySpecificAnalyzer(query.MultiLanguageEnabled, query.ContextLanguage)); // The QueryText field is intended to be a user-submitted query, so we want to be forgiving in how // we parse it. If the query parser fails to parse the value, escape it and parse it again. if (!string.IsNullOrWhiteSpace(query.QueryText)) { Query textQuery; try { textQuery = queryParser.Parse(query.QueryText); } catch (ParseException) { textQuery = queryParser.Parse(QueryParser.Escape(query.QueryText)); } booleanQuery.Add(textQuery, Occur.MUST); } // The Filter field, on the other hand, is intended to be a provided by a developer/admin, and will // therefore be parsed strictly. If this query is invalid, a ParseException will be thrown. if (!string.IsNullOrWhiteSpace(query.Filter)) { booleanQuery.Add(queryParser.Parse(query.Filter), Occur.MUST); } // If there is no user query text or filter, return no results. (A BooleanQuery with no clauses does this.) if (!booleanQuery.Any()) { return(new BooleanQuery()); } if (query.LogicalNames.Any()) { var logicalNameQuery = new BooleanQuery(); foreach (var logicalName in query.LogicalNames) { if (string.IsNullOrEmpty(logicalName)) { continue; } logicalNameQuery.Add(new TermQuery(new Term(Index.LogicalNameFieldName, logicalName)), Occur.SHOULD); } booleanQuery.Add(logicalNameQuery, Occur.MUST); } this.AddLanguageRestrictionToQuery(query, booleanQuery); return(booleanQuery); }
/// <summary> /// Gets the sort field. /// </summary> /// <param name="query">The query.</param> /// <returns>Sort Field</returns> private SortField[] GetSortField(ICrmEntityQuery query) { var sortingOption = query.SortingOption; if (sortingOption == SortingFields.Rating || sortingOption == SortingFields.ViewCount) { return(new SortField[] { new SortField(sortingOption, SortField.DOUBLE, true) }); } return(new SortField[] { SortField.FIELD_SCORE }); }
public virtual ICrmEntitySearchResultPage Search(ICrmEntityQuery query) { ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format(@"query=(PageNumber={0},PageSize={1},LogicalNames=({2}))", query.PageNumber, query.PageSize, string.Join(",", query.LogicalNames.ToArray()))); var pageNumber = query.PageNumber; if (pageNumber < 1) { ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Page number cannot be less than 1. Forcing PageNumber to 1."); pageNumber = 1; } var pageSize = query.PageSize; if (pageSize < 1) { ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Page size cannot be less than 1. Forcing PageSize to 1."); pageSize = 1; } var luceneQuery = CreateQuery(query); var resultFactory = Index.GetSearchResultFactory(luceneQuery); var results = new List <ICrmEntitySearchResult>(); // We add a +1 to the searchLimit and resultLimit so as to try and go one result beyond the requested result page, so that // approximateTotalHits will reflect whether there is at least one further valid/readable result beyond the current page. // This eliminates the edge case where a user gets a full page of results, the total hits indicates there are more results, // but there actually aren't any, leading to a blank final page of results. var userResults = GetUserSearchResults( query, ((pageSize * pageNumber) * InitialSearchLimitMultiple) + 1, 0, (pageSize * pageNumber) + 1, resultFactory, pageNumber, pageSize, results); // sprinkle these calls in for whichever events we want to trace if (FeatureCheckHelper.IsFeatureEnabled(FeatureNames.CustomerJourneyTracking)) { var queryStringArray = query.QueryText.Split('(', ')'); string queryString = queryStringArray.Length > 1 ? queryStringArray[1] : query.QueryText; PortalTrackingTrace.TraceInstance.Log(Constants.Search, queryString, string.Empty); } return(userResults); }
public SearchDataSourceSelectingEventArgs(SearchProvider provider, ICrmEntityQuery query) { if (provider == null) { throw new ArgumentNullException("provider"); } if (query == null) { throw new ArgumentNullException("query"); } Provider = provider; Query = query; }
public SearchIndexQueryDrop(IPortalLiquidContext portalLiquidContext, SearchProvider searchProvider, ICrmEntityQuery query) : base(portalLiquidContext) { if (searchProvider == null) { throw new ArgumentNullException("searchProvider"); } if (query == null) { throw new ArgumentNullException("query"); } SearchProvider = searchProvider; Query = query; _resultPage = new Lazy <ICrmEntitySearchResultPage>(GetResultPage, LazyThreadSafetyMode.None); _results = new Lazy <SearchIndexQueryResultDrop[]>(GetResults, LazyThreadSafetyMode.None); }
public ICrmEntitySearchResultPage Search(ICrmEntityQuery query) { var portal = PortalCrmConfigurationManager.CreatePortalContext(_portalName); var serviceContext = PortalCrmConfigurationManager.CreateServiceContext(_portalName); var dependencyProvider = PortalCrmConfigurationManager.CreateDependencyProvider(_portalName); var security = GetSecurityAssertion(_portalName); var metadataCache = new Dictionary <string, EntityMetadata>(); var paginator = new TopPaginator <ICrmEntitySearchResult>( query.PageSize, top => GetTopSearchResults(top, query, serviceContext, portal, security, dependencyProvider, entityName => GetEntityMetadata(serviceContext, entityName, metadataCache)), result => result != null); var results = paginator.GetPage(query.PageNumber); return(new CrmEntitySearchResultPage(results, results.TotalUnfilteredItems, query.PageNumber, query.PageSize)); }
/// <summary> /// Override to get faceted results /// </summary> /// <param name="query">The query</param> /// <param name="searchLimit">The max number of results we want</param> /// <param name="offset">The number of already processed results to skip.</param> /// <returns>The results of the search unfiltered for the user.</returns> protected override RawSearchResultSet GetRawSearchResults(ICrmEntityQuery query, int searchLimit, int offset) { var stopwatch = new Stopwatch(); stopwatch.Start(); var br = new BrowseRequest(); br.Count = searchLimit; br.Sort = this.GetSortField(query); br.Query = this.CreateQuery(query); br.Offset = offset; this.AddFacetConstraints(br, query.FacetConstraints); // add preconfigured facet specs foreach (var fieldToSpec in this.specs) { br.SetFacetSpec(fieldToSpec.Key, fieldToSpec.Value); } // execute the query IBrowsable browser = new BoboBrowser(this.boboReader); foreach (var facetConfiguration in this.config.GetConfiguredFacets()) { if (facetConfiguration.FacetHandlerType == FacetHandlerType.Dynamic) { browser.SetFacetHandler(facetConfiguration.FacetHandler); } } var browseResult = browser.Browse(br); stopwatch.Stop(); ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("Lucene(faceted/BoboBrowse): {0} total hits ({1}ms)", browseResult.NumHits, stopwatch.ElapsedMilliseconds)); PortalFeatureTrace.TraceInstance.LogSearch(FeatureTraceCategory.Search, browseResult.NumHits, stopwatch.ElapsedMilliseconds, string.Format("Lucene(faceted/BoboBrowse): {0} total hits ({1}ms)", browseResult.NumHits, stopwatch.ElapsedMilliseconds)); return(this.ConvertBoboBrowseResultsToRawSearchResultSet(browseResult, offset, query.FacetConstraints)); }
public ICrmEntitySearchResultPage Search(ICrmEntityQuery query) { var rawResults = _searcher.Search(query); var infoCache = new Dictionary <string, ExtendedAttributeSearchResultInfo>(); var metadataCache = new Dictionary <string, EntityMetadata>(); var results = rawResults.Select(result => { var info = GetExtendedAttributeInfo(_dataContextName, result.Entity.LogicalName, infoCache, metadataCache); var extendedAttributes = info.GetAttributes(result.Entity, metadataCache); return(new CrmEntitySearchResult(result.Entity, result.Score, result.ResultNumber, result.Title, result.Url, extendedAttributes) { Fragment = result.Fragment } as ICrmEntitySearchResult); }); return(new CrmEntitySearchResultPage(results, rawResults.ApproximateTotalHits, rawResults.PageNumber, rawResults.PageSize)); }
public SearchDataSourceStatusEventArgs(SearchProvider provider, ICrmEntityQuery query, ICrmEntitySearchResultPage results) { if (provider == null) { throw new ArgumentNullException("provider"); } if (query == null) { throw new ArgumentNullException("query"); } if (results == null) { throw new ArgumentNullException("results"); } Provider = provider; Query = query; Results = results; }
public ICrmEntitySearchResultPage Search(ICrmEntityQuery query) { // If we don't get a read lock quickly, just fail fast to constructing and using a new index searcher. if (!_searcherLock.TryEnterReadLock(TimeSpan.FromSeconds(1))) { ADXTrace.Instance.TraceInfo(TraceCategory.Application, "Failed to acquire read lock on shared index searcher. Creating new single-use index searcher."); using (var searcher = _searcherFactory()) { return(searcher.Search(query)); } } try { return(_searcher.Search(query)); } finally { _searcherLock.ExitReadLock(); } }
private TopPaginator <ICrmEntitySearchResult> .Top GetTopSearchResults(int top, ICrmEntityQuery query, OrganizationServiceContext serviceContext, IPortalContext portal, Func <OrganizationServiceContext, Entity, bool> assertSecurity, IDependencyProvider dependencyProvider, Func <string, EntityMetadata> getEntityMetadata) { EntitySearchResultPage searchResponse; try { searchResponse = _service.Search(query.QueryText, 1, top, string.Join(",", query.LogicalNames.ToArray()), portal.Website.Id.ToString(), query.Filter); } catch (Exception e) { ADXTrace.Instance.TraceError(TraceCategory.Application, string.Format("Service exception: {0}", e.ToString())); throw; } if (searchResponse.IndexNotFound) { throw new IndexNotFoundException("Search index not found. Please ensure that the search index is constructed before attempting a query."); } var currentResultNumber = 0; var items = searchResponse.Results.Select(result => { var metadata = getEntityMetadata(result.EntityLogicalName); if (metadata == null) { return(null); } var entity = serviceContext.CreateQuery(metadata.LogicalName) .FirstOrDefault(e => e.GetAttributeValue <Guid>(metadata.PrimaryIdAttribute) == result.EntityID); if (entity == null) { return(null); } if (!assertSecurity(serviceContext, entity)) { return(null); } var urlProvider = dependencyProvider.GetDependency <IEntityUrlProvider>(); var path = urlProvider.GetUrl(serviceContext, entity); if (path == null) { return(null); } Uri url; if (!Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out url)) { return(null); } currentResultNumber++; return(new CrmEntitySearchResult(entity, result.Score, currentResultNumber, result.Title, url) { Fragment = result.Fragment }); }); return(new TopPaginator <ICrmEntitySearchResult> .Top(items, searchResponse.ApproximateTotalHits)); }
public ICrmEntitySearchResultPage Search(ICrmEntityQuery query) { var logicalNames = query.LogicalNames.Any() ? string.Join(",", query.LogicalNames.ToArray()) : string.Empty; return(Channel.Search(query.QueryText, query.PageNumber, query.PageSize, logicalNames, SearchProvider)); }
/// <summary> /// Overrides Search behavior to do faceted search with BoboBrowse.Net /// </summary> /// <param name="query"> /// The search query. /// </param> /// <returns> /// The <see cref="Query"/>. /// </returns> protected override Query CreateQuery(ICrmEntityQuery query) { if (FeatureCheckHelper.IsFeatureEnabled(FeatureNames.CmsEnabledSearching)) { var baseQuery = base.CreateQuery(query); var compositeQuery = new BooleanQuery() { { baseQuery, Occur.MUST } }; var contentAccessLevelProvider = new ContentAccessLevelProvider(); compositeQuery.Add(new TermQuery(new Term("_logicalname", "annotation")), Occur.MUST_NOT); if (contentAccessLevelProvider.IsEnabled()) { var calQuery = new BooleanQuery(); var userCals = contentAccessLevelProvider.GetContentAccessLevels(); ADXTrace.Instance.TraceInfo(TraceCategory.Monitoring, "Adding User CALs to Lucene query"); foreach (var cal in userCals) { ADXTrace.Instance.TraceInfo(TraceCategory.Monitoring, string.Format("User CAL {0}", cal.Id)); calQuery.Add(new TermQuery(new Term(FixedFacetsConfiguration.ContentAccessLevel, cal.Id.ToString())), Occur.SHOULD); } calQuery.Add(new TermQuery(new Term(FixedFacetsConfiguration.ContentAccessLevel, "public")), Occur.SHOULD); compositeQuery.Add(calQuery, Occur.MUST); } var productAccessProvider = new ProductAccessProvider(); if (productAccessProvider.IsEnabled()) { var productFilteringQuery = new BooleanQuery { { new TermQuery( new Term(FixedFacetsConfiguration.ProductFieldFacetName, this.Index.ProductAccessNonKnowledgeArticleDefaultValue)), Occur.SHOULD } }; var userProducts = productAccessProvider.GetProducts(); ADXTrace.Instance.TraceInfo(TraceCategory.Monitoring, "Adding User products to Lucene query"); foreach (var product in userProducts) { ADXTrace.Instance.TraceInfo(TraceCategory.Monitoring, string.Format("User product {0}", product)); productFilteringQuery.Add( new TermQuery(new Term(FixedFacetsConfiguration.ProductFieldFacetName, product.ToString())), Occur.SHOULD); } if (PortalContext.Current.User != null) { if (productAccessProvider.DisplayArticlesWithoutAssociatedProductsEnabled()) { productFilteringQuery.Add( new TermQuery(new Term(FixedFacetsConfiguration.ProductFieldFacetName, this.Index.ProductAccessDefaultValue)), Occur.SHOULD); } } else { productFilteringQuery.Add( new TermQuery(new Term(FixedFacetsConfiguration.ProductFieldFacetName, "unauthenticatedUser")), Occur.SHOULD); } compositeQuery.Add(productFilteringQuery, Occur.MUST); } ADXTrace.Instance.TraceInfo(TraceCategory.Monitoring, string.Format("Adding User WebRoleDefaultValue to Lucene query: {0}", this.Index.WebRoleDefaultValue)); var cmsQuery = new BooleanQuery { { new TermQuery( new Term(this.Index.WebRoleFieldName, this.Index.WebRoleDefaultValue)), Occur.SHOULD } }; // Windows Live ID Server decided to return null for an unauthenticated user's name // A null username, however, breaks the Roles.GetRolesForUser() because it expects an empty string. var currentUsername = (HttpContext.Current != null && HttpContext.Current.User != null && HttpContext.Current.User.Identity != null) ? HttpContext.Current.User.Identity.Name ?? string.Empty : string.Empty; var userRoles = Roles.GetRolesForUser(currentUsername); ADXTrace.Instance.TraceInfo(TraceCategory.Monitoring, "Adding user role to Lucene query"); foreach (var role in userRoles) { ADXTrace.Instance.TraceInfo(TraceCategory.Monitoring, string.Format("User role: {0}", role)); cmsQuery.Add(new TermQuery(new Term(this.Index.WebRoleFieldName, role)), Occur.SHOULD); } compositeQuery.Add(cmsQuery, Occur.MUST); // Add the Url Defined Part to the Query. var urlDefinedQuery = new BooleanQuery { { new TermQuery( new Term(this.Index.IsUrlDefinedFieldName, bool.TrueString)), Occur.SHOULD } }; compositeQuery.Add(urlDefinedQuery, Occur.MUST); // Add knowledgearticle to the query compositeQuery.Add( new TermQuery(new Term(FixedFacetsConfiguration.RecordTypeFacetFieldName, FixedFacetsConfiguration.KnowledgeArticleConstraintName)), Occur.SHOULD); return(compositeQuery); } else { return(base.CreateQuery(query)); } }
private IEnumerable <Document> GetRelatedAnnotations(RawSearchResultSet rawSearchResults, ICrmEntityQuery query) { Query textQuery; var noteQuery = new BooleanQuery(); var queryParser = new QueryParser(Index.Version, Index.ContentFieldName, Index.GetQuerySpecificAnalyzer(query.MultiLanguageEnabled, query.ContextLanguage)); try { textQuery = queryParser.Parse(string.Format("+({0}) filename:({0}) notetext:({0}) _logicalname:annotation~0.9^0.3", query.QueryTerm)); } catch (ParseException) { textQuery = queryParser.Parse(QueryParser.Escape(string.Format("+({0}) filename:({0}) notetext:({0}) _logicalname:annotation~0.9^0.3", query.QueryTerm))); } noteQuery.Add(textQuery, Occur.MUST); noteQuery.Add(new TermQuery(new Term("_logicalname", "annotation")), Occur.MUST); foreach (var scoreDoc in rawSearchResults.Results) { var resultField = _searcher.Doc(scoreDoc.Doc).GetField("_logicalname"); if (resultField != null && resultField.StringValue == "knowledgearticle") { var primaryKey = _searcher.Doc(scoreDoc.Doc).GetField("_primarykey"); noteQuery.Add(new TermQuery(new Term("annotation_knowledgearticleid", primaryKey.StringValue)), Occur.SHOULD); } } var rawNoteResults = GetRawSearchResults(noteQuery, 30, 0); return(rawNoteResults.Results.Select(rawNoteResult => _searcher.Doc(rawNoteResult.Doc)).ToList()); }
/// <summary> /// Get the processed search results provided by the query available to the searching user. /// </summary> /// <param name="query">Search query.</param> /// <param name="searchLimit">Number of results to obtain from the underlying search library.</param> /// <param name="initialOffset">Number of already processed results to skip.</param> /// <param name="resultLimit">Number of results to return in the result page.</param> /// <param name="resultFactory">Factory to generate the ICrmEntitySearchResults</param> /// <param name="pageNumber">Page number</param> /// <param name="pageSize">Page size</param> /// <param name="results">Processed results so far.</param> /// <returns></returns> protected ICrmEntitySearchResultPage GetUserSearchResults(ICrmEntityQuery query, int searchLimit, int initialOffset, int resultLimit, ICrmEntitySearchResultFactory resultFactory, int pageNumber, int pageSize, ICollection <ICrmEntitySearchResult> results) { ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("(searchLimit={0},rawOffset={1},resultLimit={2})", searchLimit, initialOffset, resultLimit)); RawSearchResultSet rawSearchResults = GetRawSearchResults(query, searchLimit, initialOffset); if (initialOffset >= rawSearchResults.TotalHits) { return(GenerateResultPage(results, rawSearchResults.TotalHits, pageNumber, pageSize, rawSearchResults)); } var stopwatch = new Stopwatch(); stopwatch.Start(); var groupedNotes = new List <IGrouping <EntityReference, ICrmEntitySearchResult> >(); var displayNotes = IsAnnotationSearchEnabled(); if (displayNotes && !string.IsNullOrEmpty(query.QueryTerm)) { var rawNotes = this.GetRelatedAnnotations(rawSearchResults, query); var notes = rawNotes.Select(document => resultFactory.GetResult(document, 1, results.Count + 1)).ToList(); //Grouping Notes by related Knowledge Articles groupedNotes = notes.Where(note => note.EntityLogicalName == "annotation") .GroupBy(note => note.Entity.GetAttributeValue <EntityReference>("objectid")) .ToList(); } var offsetForNextIteration = initialOffset; foreach (var scoreDoc in rawSearchResults.Results) { offsetForNextIteration++; var result = resultFactory.GetResult(_searcher.Doc(scoreDoc.Doc), scoreDoc.Score, results.Count + 1); // Not a valid user result, filter out if (result == null) { continue; } if (result.EntityLogicalName == "knowledgearticle" && displayNotes) { var relatedNotes = groupedNotes.Where(a => a.Key.Id == result.EntityID).SelectMany(i => i).Take(3).ToList(); if (relatedNotes.Any(note => note.Fragment == result.Fragment)) { result.Fragment = GetKnowledgeArticleDescription(result); } result.Entity["relatedNotes"] = relatedNotes; } results.Add(result); if (results.Count >= resultLimit) { stopwatch.Stop(); ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("Gathered {0} results, done ({1}ms)", results.Count, stopwatch.ElapsedMilliseconds)); PortalFeatureTrace.TraceInstance.LogSearch(FeatureTraceCategory.Search, results.Count, stopwatch.ElapsedMilliseconds, string.Format("Gathered {0} results, done ({1}ms)", results.Count, stopwatch.ElapsedMilliseconds)); return(GenerateResultPage(results, rawSearchResults.TotalHits, pageNumber, pageSize, rawSearchResults)); } } stopwatch.Stop(); // We asked for more hits than we got back from Lucene, and we still didn't gather enough valid // results. That's all we're going to get, so the number of results we got is the number of hits. if (searchLimit >= rawSearchResults.TotalHits) { ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("All available results ({0}) gathered, done ({1}ms)", results.Count, stopwatch.ElapsedMilliseconds)); PortalFeatureTrace.TraceInstance.LogSearch(FeatureTraceCategory.Search, results.Count, stopwatch.ElapsedMilliseconds, string.Format("All available results ({0}) gathered, done ({1}ms)", results.Count, stopwatch.ElapsedMilliseconds)); return(GenerateResultPage(results, results.Count, pageNumber, pageSize, rawSearchResults)); } ADXTrace.Instance.TraceInfo(TraceCategory.Application, string.Format("{0} results gathered so far ({1}ms)", results.Count, stopwatch.ElapsedMilliseconds)); PortalFeatureTrace.TraceInstance.LogSearch(FeatureTraceCategory.Search, results.Count, stopwatch.ElapsedMilliseconds, string.Format("{0} results gathered so far ({1}ms)", results.Count, stopwatch.ElapsedMilliseconds)); return(GetUserSearchResults(query, searchLimit * ExtendedSearchLimitMultiple, offsetForNextIteration, resultLimit, resultFactory, pageNumber, pageSize, results)); }