/// <summary> /// Returns a list of matching people /// </summary> /// <param name="searchterm"></param> /// <returns></returns> public override IQueryable <string> Search(string searchterm) { // get configured entities and turn it into a list of entity ids List <int> entityIds = new List <int>(); var searchEntitiesSetting = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchEntities"); if (!string.IsNullOrWhiteSpace(searchEntitiesSetting)) { entityIds = searchEntitiesSetting.Split(',').Select(int.Parse).ToList(); } // get the field critiera var fieldCriteriaSetting = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchFieldCriteria"); SearchFieldCriteria fieldCriteria = new SearchFieldCriteria(); // get the search type var searchType = SearchType.Wildcard; if (!string.IsNullOrWhiteSpace(Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchSearchType"))) { searchType = (SearchType)Enum.Parse(typeof(SearchType), Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchSearchType")); } if (!string.IsNullOrWhiteSpace(fieldCriteriaSetting)) { foreach (var queryString in fieldCriteriaSetting.ToKeyValuePairList()) { // check that multiple values were not passed var values = queryString.Value.ToString().Split(','); foreach (var value in values) { fieldCriteria.FieldValues.Add(new FieldValue { Field = queryString.Key, Value = value }); } } } var client = IndexContainer.GetActiveComponent(); var results = client.Search(searchterm, searchType, entityIds, fieldCriteria); // NOTE: Put a bunch of whitespace before and after it so that the Search box shows blank instead of stringified html return(results.Select(r => $" <data return-type='{r.IndexModelType}' return-id={r.Id}></data><i class='{ r.IconCssClass}'></i> {r.DocumentName} ").ToList().AsQueryable()); }
/// <summary> /// Gets the search result queryable that matches the search term. /// </summary> /// <param name="searchTerm">The search term used to find results.</param> /// <returns>A queryable of index models that match the search term.</returns> private List <UniversalSearch.IndexModels.IndexModelBase> GetSearchResults(string searchTerm) { // get configured entities and turn it into a list of entity ids List <int> entityIds = new List <int>(); var searchEntitiesSetting = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchEntities"); if (!string.IsNullOrWhiteSpace(searchEntitiesSetting)) { entityIds = searchEntitiesSetting.Split(',').Select(int.Parse).ToList(); } // get the field criteria var fieldCriteriaSetting = Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchFieldCriteria"); SearchFieldCriteria fieldCriteria = new SearchFieldCriteria(); // get the search type var searchType = SearchType.Wildcard; if (!string.IsNullOrWhiteSpace(Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchSearchType"))) { searchType = ( SearchType )Enum.Parse(typeof(SearchType), Rock.Web.SystemSettings.GetValue("core_SmartSearchUniversalSearchSearchType")); } if (!string.IsNullOrWhiteSpace(fieldCriteriaSetting)) { foreach (var queryString in fieldCriteriaSetting.ToKeyValuePairList()) { // check that multiple values were not passed var values = queryString.Value.ToString().Split(','); foreach (var value in values) { fieldCriteria.FieldValues.Add(new FieldValue { Field = queryString.Key, Value = value }); } } } var client = IndexContainer.GetActiveComponent(); return(client.Search(searchTerm, searchType, entityIds, fieldCriteria)); }
/// <summary> /// Returns a list of matching people /// </summary> /// <param name="searchterm"></param> /// <returns></returns> public override IQueryable<string> Search( string searchterm ) { // get configured entities and turn it into a list of entity ids List<int> entityIds = new List<int>(); var searchEntitiesSetting = Rock.Web.SystemSettings.GetValue( "core_SmartSearchUniversalSearchEntities" ); if ( !string.IsNullOrWhiteSpace( searchEntitiesSetting ) ) { entityIds = searchEntitiesSetting.Split( ',' ).Select( int.Parse ).ToList(); } // get the field critiera var fieldCriteriaSetting = Rock.Web.SystemSettings.GetValue( "core_SmartSearchUniversalSearchFieldCriteria" ); SearchFieldCriteria fieldCriteria = new SearchFieldCriteria(); // get the search type var searchType = SearchType.Wildcard; if ( !string.IsNullOrWhiteSpace( Rock.Web.SystemSettings.GetValue( "core_SmartSearchUniversalSearchFieldCriteria" ) ) ) { searchType = (SearchType)Enum.Parse( typeof( SearchType ), Rock.Web.SystemSettings.GetValue( "core_SmartSearchUniversalSearchFieldCriteria" ) ); } if ( !string.IsNullOrWhiteSpace( fieldCriteriaSetting ) ) { foreach ( var queryString in fieldCriteriaSetting.ToKeyValuePairList() ) { // check that multiple values were not passed var values = queryString.Value.ToString().Split( ',' ); foreach ( var value in values ) { fieldCriteria.FieldValues.Add( new FieldValue { Field = queryString.Key, Value = value } ); } } } var client = IndexContainer.GetActiveComponent(); var results = client.Search( searchterm, SearchType.Wildcard, entityIds, fieldCriteria ); return results.Select( r => $" <data return-type='{r.IndexModelType}' return-id={r.Id}></data><i class='{ r.IconCssClass}'></i> {r.DocumentName} " ).ToList().AsQueryable(); }
/// <summary> /// Performs the search /// </summary> private void Search( ) { var term = PageParameter( "Q" ); lResults.Text = string.Empty; var searchType = GetAttributeValue( "SearchType" ).ConvertToEnum<SearchType>(); // get listing of selected entities to search on List<int> selectedEntities = GetSearchEntities(); // get listing of filters to apply List<FieldValue> fieldValues = GetFieldFilters( selectedEntities ); SearchFieldCriteria fieldCriteria = new SearchFieldCriteria(); fieldCriteria.FieldValues = fieldValues; var client = IndexContainer.GetActiveComponent(); long totalResultsAvailable = 0; var results = client.Search( term, searchType, selectedEntities, fieldCriteria, _itemsPerPage, _currentPageNum * _itemsPerPage, out totalResultsAvailable ); StringBuilder formattedResults = new StringBuilder(); formattedResults.Append( "<ul class='list-unstyled'>" ); foreach ( var result in results) { var formattedResult = result.FormatSearchResult( CurrentPerson ); if ( formattedResult.IsViewAllowed ) { formattedResults.Append( string.Format( "{0}", formattedResult.FormattedResult )); if ( GetAttributeValue( "ShowScores" ).AsBoolean() ) { formattedResults.Append( string.Format( "<div class='pull-right'><small>{0}</small></div>", result.Score ) ); } formattedResults.Append( "<hr />" ); } } formattedResults.Append( "</ul>" ); tbSearch.Text = term; lResults.Text = formattedResults.ToString(); // pageination StringBuilder pagination = new StringBuilder(); // previous button if (_currentPageNum == 0 ) { pagination.Append("<li class='disabled'><span><span aria-hidden='true'>«</span></span></li>"); } else { pagination.Append( String.Format("<li><a href='{0}'><span><span aria-hidden='true'>«</span></span></a></li>", BuildUrl(-1)) ); } var paginationOffset = 5; var startPage = 1; var endPage = paginationOffset * 2; if (_currentPageNum >= paginationOffset ) { startPage = _currentPageNum - paginationOffset; endPage = _currentPageNum + paginationOffset; } if ((endPage * _itemsPerPage) > totalResultsAvailable ) { endPage = (int)Math.Ceiling((double)totalResultsAvailable / _itemsPerPage); } if ( endPage == 1 ) { pnlPagination.Visible = false; return; } for ( int i = startPage; i <= endPage; i++ ) { if (_currentPageNum == i ) { pagination.Append( string.Format( "<li class='active'><span>{0} </span></li>", i ) ); } else { pagination.Append( string.Format( "<li><a href='{1}'><span>{0} </span></a></li>", i, BuildUrl((i - _currentPageNum) - 1) ) ); } } // next button if ( _currentPageNum == endPage ) { pagination.Append( "<li class='disabled'><span><span aria-hidden='true'>»</span></span></li>" ); } else { pagination.Append( String.Format( "<li><a href='{0}'><span><span aria-hidden='true'>»</span></span></a></li>", BuildUrl( 1 ) ) ); } lPagination.Text = pagination.ToString(); }
/// <summary> /// Searches the specified query. /// </summary> /// <param name="query">The query.</param> /// <param name="searchType">Type of the search.</param> /// <param name="entities">The entities.</param> /// <param name="fieldCriteria">The field criteria.</param> /// <param name="size">The size.</param> /// <param name="from">From.</param> /// <param name="totalResultsAvailable">The total results available.</param> /// <returns></returns> public override List <IndexModelBase> Search(string query, SearchType searchType, List <int> entities, SearchFieldCriteria fieldCriteria, int?size, int?from, out long totalResultsAvailable) { List <IndexModelBase> documents = new List <IndexModelBase>(); totalResultsAvailable = 0; if (_client != null) { ISearchResponse <dynamic> results = null; List <SearchResultModel> searchResults = new List <SearchResultModel>(); QueryContainer queryContainer = new QueryContainer(); // add and field constraints var searchDescriptor = new SearchDescriptor <dynamic>().AllIndices(); if (entities == null || entities.Count == 0) { searchDescriptor = searchDescriptor.AllTypes(); } else { var entityTypes = new List <string>(); foreach (var entityId in entities) { // get entities search model name var entityType = new EntityTypeService(new RockContext()).Get(entityId); entityTypes.Add(entityType.IndexModelType.Name.ToLower()); // check if this is a person model, if so we need to add two model types one for person and the other for businesses // wish there was a cleaner way to do this if (entityType.Guid == SystemGuid.EntityType.PERSON.AsGuid()) { entityTypes.Add("businessindex"); } } searchDescriptor = searchDescriptor.Type(string.Join(",", entityTypes)); // todo: consider adding indexmodeltype to the entity cache } QueryContainer matchQuery = null; if (fieldCriteria != null && fieldCriteria.FieldValues?.Count > 0) { foreach (var match in fieldCriteria.FieldValues) { if (fieldCriteria.SearchType == CriteriaSearchType.Or) { matchQuery |= new MatchQuery { Field = match.Field, Query = match.Value, Boost = match.Boost }; } else { matchQuery &= new MatchQuery { Field = match.Field, Query = match.Value }; } } } switch (searchType) { case SearchType.ExactMatch: { if (!string.IsNullOrWhiteSpace(query)) { queryContainer &= new QueryStringQuery { Query = query, AnalyzeWildcard = true }; } // special logic to support emails if (query.Contains("@")) { queryContainer |= new QueryStringQuery { Query = "email:" + query, Analyzer = "whitespace" }; // analyzer = whitespace to keep the email from being parsed into 3 variables because the @ will act as a delimitor by default } // special logic to support phone search if (query.IsDigitsOnly()) { queryContainer |= new QueryStringQuery { Query = "phone:*" + query + "*", AnalyzeWildcard = true }; } // add a search for all the words as one single search term queryContainer |= new QueryStringQuery { Query = query, AnalyzeWildcard = true, PhraseSlop = 0 }; if (matchQuery != null) { queryContainer &= matchQuery; } if (size.HasValue) { searchDescriptor.Size(size.Value); } if (from.HasValue) { searchDescriptor.From(from.Value); } searchDescriptor.Query(q => queryContainer); results = _client.Search <dynamic>(searchDescriptor); break; } case SearchType.Fuzzy: { results = _client.Search <dynamic>(d => d.AllIndices().AllTypes() .Query(q => q.Fuzzy(f => f.Value(query) .Rewrite(RewriteMultiTerm.TopTermsN)) ) ); break; } case SearchType.Wildcard: { bool enablePhraseSearch = true; if (!string.IsNullOrWhiteSpace(query)) { QueryContainer wildcardQuery = null; // break each search term into a separate query and add the * to the end of each var queryTerms = query.Split(' ').Select(p => p.Trim()).ToList(); // special logic to support emails if (queryTerms.Count == 1 && query.Contains("@")) { wildcardQuery |= new QueryStringQuery { Query = "email:*" + query + "*", Analyzer = "whitespace" }; enablePhraseSearch = false; } else { foreach (var queryTerm in queryTerms) { if (!string.IsNullOrWhiteSpace(queryTerm)) { wildcardQuery &= new QueryStringQuery { Query = queryTerm + "*", Analyzer = "whitespace", Rewrite = RewriteMultiTerm.ScoringBoolean }; // without the rewrite all results come back with the score of 1; analyzer of whitespaces says don't fancy parse things like check-in to 'check' and 'in' } } // add special logic to help boost last names if (queryTerms.Count > 1) { QueryContainer nameQuery = null; nameQuery &= new QueryStringQuery { Query = "lastName:" + queryTerms.Last() + "*", Analyzer = "whitespace", Boost = 30 }; nameQuery &= new QueryStringQuery { Query = "firstName:" + queryTerms.First() + "*", Analyzer = "whitespace" }; wildcardQuery |= nameQuery; } // special logic to support phone search if (query.IsDigitsOnly()) { wildcardQuery |= new QueryStringQuery { Query = "phoneNumbers:*" + query, Analyzer = "whitespace" }; } } queryContainer &= wildcardQuery; } // add a search for all the words as one single search term if (enablePhraseSearch) { queryContainer |= new QueryStringQuery { Query = query, AnalyzeWildcard = true, PhraseSlop = 0 }; } if (matchQuery != null) { queryContainer &= matchQuery; } if (size.HasValue) { searchDescriptor.Size(size.Value); } if (from.HasValue) { searchDescriptor.From(from.Value); } searchDescriptor.Query(q => queryContainer); var indexBoost = GlobalAttributesCache.Value("UniversalSearchIndexBoost"); if (indexBoost.IsNotNullOrWhiteSpace()) { var boostItems = indexBoost.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); foreach (var boostItem in boostItems) { var boostParms = boostItem.Split(new char[] { '^' }); if (boostParms.Length == 2) { int boost = 1; Int32.TryParse(boostParms[1], out boost); searchDescriptor.IndicesBoost(b => b.Add(boostParms[0], boost)); } } } results = _client.Search <dynamic>(searchDescriptor); break; } } totalResultsAvailable = results.Total; // normalize the results to rock search results if (results != null) { foreach (var hit in results.Hits) { IndexModelBase document = new IndexModelBase(); try { if (hit.Source != null) { Type indexModelType = Type.GetType($"{ ((string)((JObject)hit.Source)["indexModelType"])}, { ((string)((JObject)hit.Source)["indexModelAssembly"])}"); if (indexModelType != null) { document = (IndexModelBase)((JObject)hit.Source).ToObject(indexModelType); // return the source document as the derived type } else { document = ((JObject)hit.Source).ToObject <IndexModelBase>(); // return the source document as the base type } } if (hit.Explanation != null) { document["Explain"] = hit.Explanation.ToJson(); } document.Score = hit.Score; documents.Add(document); } catch { } // ignore if the result if an exception resulted (most likely cause is getting a result from a non-rock index) } } } return(documents); }
/// <summary> /// Searches the specified query. /// </summary> /// <param name="query">The query.</param> /// <param name="searchType">Type of the search.</param> /// <param name="entities">The entities.</param> /// <param name="fieldCriteria">The field criteria.</param> /// <param name="size">The size.</param> /// <param name="from">From.</param> /// <returns></returns> public override List <IndexModelBase> Search(string query, SearchType searchType = SearchType.Wildcard, List <int> entities = null, SearchFieldCriteria fieldCriteria = null, int?size = null, int?from = null) { long totalResultsAvailable = 0; return(Search(query, searchType, entities, fieldCriteria, size, from, out totalResultsAvailable)); }
/// <summary> /// Renders the specified context. /// </summary> /// <param name="context">The context.</param> /// <param name="result">The result.</param> public override void Render( Context context, TextWriter result ) { // first ensure that search commands are allowed in the context if ( !this.IsAuthorized( context ) ) { result.Write( string.Format( "The Lava command '{0}' is not configured for this template.", this.Name ) ); base.Render( context, result ); return; } var parms = ParseMarkup( _markup, context ); SearchFieldCriteria fieldCriteria = new SearchFieldCriteria(); SearchType searchType = SearchType.Wildcard; List<int> entityIds = new List<int>(); string query = string.Empty; if (parms.Any( p => p.Key == "query" ) ) { query = parms["query"]; } if ( parms.Any( p => p.Key == "fieldcriteria" ) ) { foreach ( var queryString in parms["fieldcriteria"].ToKeyValuePairList() ) { // check that multiple values were not passed var values = queryString.Value.ToString().Split( ',' ); foreach ( var value in values ) { fieldCriteria.FieldValues.Add( new FieldValue { Field = queryString.Key, Value = value } ); } } } if ( parms.Any( p => p.Key == "searchtype" ) ) { switch( parms["searchtype"] ) { case "exactmatch": { searchType = SearchType.ExactMatch; break; } case "fuzzy": { searchType = SearchType.Fuzzy; break; } case "wildcard": { searchType = SearchType.Wildcard; break; } } } if ( parms.Any( p => p.Key == "criteriasearchtype" ) ) { if (parms["criteriasearchtype"].ToLower() == "and" ) { fieldCriteria.SearchType = CriteriaSearchType.And; } } if ( parms.Any( p => p.Key == "entities" ) ) { var entities = parms["entities"].Split( ',' ); foreach(var entity in entities ) { foreach(var entityType in EntityTypeCache.All() ) { if (entityType.FriendlyName.ToLower() == entity ) { entityIds.Add( entityType.Id ); } } } } var client = IndexContainer.GetActiveComponent(); var results = client.Search( query, searchType, entityIds, fieldCriteria ); context.Scopes.Last()[parms["iterator"]] = results; base.Render( context, result ); }
/// <summary> /// Searches the specified query. /// </summary> /// <param name="query">The query.</param> /// <param name="searchType">Type of the search.</param> /// <param name="entities">The entities.</param> /// <param name="fieldCriteria">The field criteria.</param> /// <param name="size">The size.</param> /// <param name="from">From.</param> /// <param name="totalResultsAvailable">The total results available.</param> /// <returns></returns> public override List <IndexModelBase> Search(string query, SearchType searchType, List <int> entities, SearchFieldCriteria fieldCriteria, int?size, int?from, out long totalResultsAvailable) { List <IndexModelBase> documents = new List <IndexModelBase>(); totalResultsAvailable = 0; bool allEntities = false; BooleanQuery queryContainer = new BooleanQuery(); List <string> combinedFields = new List <string>(); List <Type> indexModelTypes = new List <Type>(); Dictionary <string, Analyzer> combinedFieldAnalyzers = new Dictionary <string, Analyzer>(); using (RockContext rockContext = new RockContext()) { var entityTypeService = new EntityTypeService(rockContext); if (entities == null || entities.Count == 0) { //add all entities allEntities = true; var selectedEntityTypes = EntityTypeCache.All().Where(e => e.IsIndexingSupported && e.IsIndexingEnabled && e.FriendlyName != "Site"); foreach (var entityTypeCache in selectedEntityTypes) { entities.Add(entityTypeCache.Id); } } foreach (var entityId in entities) { // get entities search model name var entityType = entityTypeService.GetNoTracking(entityId); indexModelTypes.Add(entityType.IndexModelType); // check if this is a person model, if so we need to add two model types one for person and the other for businesses // wish there was a cleaner way to do this if (entityType.Guid == SystemGuid.EntityType.PERSON.AsGuid()) { indexModelTypes.Add(typeof(BusinessIndex)); } } indexModelTypes = indexModelTypes.Distinct().ToList(); CombineIndexTypes(indexModelTypes, out combinedFields, out combinedFieldAnalyzers); if (entities != null && entities.Count != 0 && !allEntities) { var indexModelTypesQuery = new BooleanQuery(); indexModelTypes.ForEach(f => indexModelTypesQuery.Add(new TermQuery(new Term("type", f.Name.ToLower())), Occur.SHOULD)); queryContainer.Add(indexModelTypesQuery, Occur.MUST); } } TopDocs topDocs = null; // Use the analyzer in fieldAnalyzers if that field is in that dictionary, otherwise use StandardAnalyzer. PerFieldAnalyzerWrapper analyzer = new PerFieldAnalyzerWrapper(defaultAnalyzer: new StandardAnalyzer(_matchVersion), fieldAnalyzers: combinedFieldAnalyzers); if (fieldCriteria != null && fieldCriteria.FieldValues?.Count > 0) { Occur occur = fieldCriteria.SearchType == CriteriaSearchType.And ? Occur.MUST : Occur.SHOULD; foreach (var match in fieldCriteria.FieldValues) { BooleanClause booleanClause = new BooleanClause(new TermQuery(new Term(match.Field, match.Value)), occur); booleanClause.Query.Boost = match.Boost; queryContainer.Add(booleanClause); } } switch (searchType) { case SearchType.ExactMatch: { var wordQuery = new BooleanQuery(); if (!string.IsNullOrWhiteSpace(query)) { var words = query.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (var word in words) { var innerQuery = new BooleanQuery(); combinedFields.ForEach(f => innerQuery.Add(new PrefixQuery(new Term(f, word.ToLower())), Occur.SHOULD)); wordQuery.Add(innerQuery, Occur.SHOULD); } } if (wordQuery.Count() != 0) { queryContainer.Add(wordQuery, Occur.MUST); } // special logic to support emails if (query.Contains("@")) { queryContainer.Add(new BooleanClause(new TermQuery(new Term("Email", query)), Occur.SHOULD)); } // special logic to support phone search if (query.IsDigitsOnly()) { queryContainer.Add(new BooleanClause(new WildcardQuery(new Term("PhoneNumbers", "*" + query + "*")), Occur.SHOULD)); } // add a search for all the words as one single search term foreach (var field in combinedFields) { var phraseQuery = new PhraseQuery(); phraseQuery.Add(new Term(field, query.ToLower())); queryContainer.Add(phraseQuery, Occur.SHOULD); } break; } case SearchType.Fuzzy: { foreach (var field in combinedFields) { queryContainer.Add(new FuzzyQuery(new Term(field, query.ToLower())), Occur.SHOULD); } break; } case SearchType.Wildcard: { bool enablePhraseSearch = true; if (!string.IsNullOrWhiteSpace(query)) { BooleanQuery wildcardQuery = new BooleanQuery(); // break each search term into a separate query and add the * to the end of each var queryTerms = query.Split(' ').Select(p => p.Trim()).ToList(); // special logic to support emails if (queryTerms.Count == 1 && query.Contains("@")) { wildcardQuery.Add(new WildcardQuery(new Term("Email", "*" + query.ToLower() + "*")), Occur.SHOULD); enablePhraseSearch = false; } else { foreach (var queryTerm in queryTerms) { if (!string.IsNullOrWhiteSpace(queryTerm)) { var innerQuery = new BooleanQuery(); combinedFields.ForEach(f => innerQuery.Add(new PrefixQuery(new Term(f, queryTerm.ToLower())), Occur.SHOULD)); wildcardQuery.Add(innerQuery, Occur.MUST); } } // add special logic to help boost last names if (queryTerms.Count() > 1 && (indexModelTypes.Contains(typeof(PersonIndex)) || indexModelTypes.Contains(typeof(BusinessIndex)))) { BooleanQuery nameQuery = new BooleanQuery { { new PrefixQuery(new Term("FirstName", queryTerms.First().ToLower())), Occur.MUST }, { new PrefixQuery(new Term("LastName", queryTerms.Last().ToLower())) { Boost = 30 }, Occur.MUST } }; wildcardQuery.Add(nameQuery, Occur.SHOULD); nameQuery = new BooleanQuery { { new PrefixQuery(new Term("NickName", queryTerms.First().ToLower())), Occur.MUST }, { new PrefixQuery(new Term("LastName", queryTerms.Last().ToLower())) { Boost = 30 }, Occur.MUST } }; wildcardQuery.Add(nameQuery, Occur.SHOULD); } // special logic to support phone search if (query.IsDigitsOnly()) { wildcardQuery.Add(new PrefixQuery(new Term("PhoneNumbers", queryTerms.First().ToLower())), Occur.SHOULD); } } queryContainer.Add(wildcardQuery, Occur.MUST); } // add a search for all the words as one single search term if (enablePhraseSearch) { // add a search for all the words as one single search term foreach (var field in combinedFields) { var phraseQuery = new PhraseQuery(); phraseQuery.Add(new Term(field, query.ToLower())); queryContainer.Add(phraseQuery, Occur.SHOULD); } } break; } } int returnSize = 10; if (size.HasValue) { returnSize = size.Value; } OpenReader(); if (from.HasValue) { TopScoreDocCollector collector = TopScoreDocCollector.Create(returnSize * 10, true); // Search for 10 pages with returnSize entries in each page _indexSearcher.Search(queryContainer, collector); topDocs = collector.GetTopDocs(from.Value, returnSize); } else { topDocs = _indexSearcher.Search(queryContainer, returnSize); } totalResultsAvailable = topDocs.TotalHits; if (topDocs != null) { foreach (var hit in topDocs.ScoreDocs) { var document = LuceneDocToIndexModel(queryContainer, hit); if (document != null) { documents.Add(document); } } } return(documents); }
/// <summary> /// Renders the specified context. /// </summary> /// <param name="context">The context.</param> /// <param name="result">The result.</param> public override void Render(Context context, TextWriter result) { // first ensure that search commands are allowed in the context if (!this.IsAuthorized(context)) { result.Write(string.Format(RockLavaBlockBase.NotAuthorizedMessage, this.Name)); base.Render(context, result); return; } var parms = ParseMarkup(_markup, context); SearchFieldCriteria fieldCriteria = new SearchFieldCriteria(); SearchType searchType = SearchType.Wildcard; List <int> entityIds = new List <int>(); string query = string.Empty; int limit = 50; int offset = 0; if (parms.Any(p => p.Key == "query")) { query = parms["query"]; } if (parms.Any(p => p.Key == "limit")) { Int32.TryParse(parms["limit"], out limit); } if (parms.Any(p => p.Key == "offset")) { Int32.TryParse(parms["offset"], out offset); } if (parms.Any(p => p.Key == "fieldcriteria")) { foreach (var queryString in parms["fieldcriteria"].ToKeyValuePairList()) { // check that multiple values were not passed var values = queryString.Value.ToString().Split(','); foreach (var value in values) { // the first letter of the field name should be lowercase string fieldName = Char.ToLowerInvariant(queryString.Key[0]) + queryString.Key.Substring(1); fieldCriteria.FieldValues.Add(new FieldValue { Field = fieldName, Value = value }); } } } if (parms.Any(p => p.Key == "searchtype")) { switch (parms["searchtype"]) { case "exactmatch": { searchType = SearchType.ExactMatch; break; } case "fuzzy": { searchType = SearchType.Fuzzy; break; } case "wildcard": { searchType = SearchType.Wildcard; break; } } } if (parms.Any(p => p.Key == "criteriasearchtype")) { if (parms["criteriasearchtype"].ToLower() == "and") { fieldCriteria.SearchType = CriteriaSearchType.And; } } if (parms.Any(p => p.Key == "entities")) { var entities = parms["entities"].Split(','); foreach (var entity in entities) { foreach (var entityType in EntityTypeCache.All()) { if (entityType.FriendlyName?.ToLower() == entity) { entityIds.Add(entityType.Id); } } } } var client = IndexContainer.GetActiveComponent(); var results = client.Search(query, searchType, entityIds, fieldCriteria, limit, offset); context.Scopes.Last()[parms["iterator"]] = results; base.Render(context, result); }
/// <summary> /// Performs the search /// </summary> private void Search( ) { var term = PageParameter("Q"); lResults.Text = string.Empty; var searchType = GetAttributeValue("SearchType").ConvertToEnum <SearchType>(); // get listing of selected entities to search on List <int> selectedEntities = GetSearchEntities(); // get listing of filters to apply List <FieldValue> fieldValues = GetFieldFilters(selectedEntities); SearchFieldCriteria fieldCriteria = new SearchFieldCriteria(); fieldCriteria.FieldValues = fieldValues; var client = IndexContainer.GetActiveComponent(); if (client == null) { nbWarnings.Text = "No indexing service is currently configured."; } else { long totalResultsAvailable = 0; var results = client.Search(term, searchType, selectedEntities, fieldCriteria, _itemsPerPage, _currentPageNum * _itemsPerPage, out totalResultsAvailable); if (GetAttributeValue("UseCustomResults").AsBoolean()) { var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null); mergeFields.Add("Results", results); lResults.Text = GetAttributeValue("LavaResultTemplate").ResolveMergeFields(mergeFields, GetAttributeValue("CustomResultsCommands")); } else { StringBuilder formattedResults = new StringBuilder(); formattedResults.Append("<ul class='list-unstyled'>"); var showScores = GetAttributeValue("ShowScores").AsBoolean(); // for speed we will get the common merge fields and pass them to the formatter so it does not have to be done repeatedly in the loop (it's a bit expensive) var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields(null, CurrentPerson); foreach (var result in results) { var formattedResult = result.FormatSearchResult(CurrentPerson, null, mergeFields); if (formattedResult.IsViewAllowed) { formattedResults.Append(string.Format("{0}", formattedResult.FormattedResult)); if (showScores) { formattedResults.Append(string.Format("<div class='pull-right'><small>{0}</small></div>", result.Score)); } formattedResults.Append("<hr />"); } } formattedResults.Append("</ul>"); tbSearch.Text = term; lResults.Text = formattedResults.ToString(); } // pagination if (totalResultsAvailable > 0) { StringBuilder pagination = new StringBuilder(); // previous button if (_currentPageNum == 0) { pagination.Append("<li class='disabled'><span><span aria-hidden='true'>«</span></span></li>"); } else { pagination.Append(String.Format("<li><a href='{0}'><span><span aria-hidden='true'>«</span></span></a></li>", BuildUrl(-1))); } var paginationOffset = 5; var startPage = 1; var endPage = paginationOffset * 2; if (_currentPageNum >= paginationOffset) { startPage = _currentPageNum - paginationOffset; endPage = _currentPageNum + paginationOffset; } if ((endPage * _itemsPerPage) > totalResultsAvailable) { endPage = ( int )Math.Ceiling(( double )totalResultsAvailable / _itemsPerPage); } if (endPage == 1) { pnlPagination.Visible = false; return; } for (int i = startPage; i <= endPage; i++) { if ((_currentPageNum + 1) == i) { pagination.Append(string.Format("<li class='active'><span>{0} </span></li>", i)); } else { pagination.Append(string.Format("<li><a href='{1}'><span>{0} </span></a></li>", i, BuildUrl((i - _currentPageNum) - 1))); } } // next button if (_currentPageNum == endPage) { pagination.Append("<li class='disabled'><span><span aria-hidden='true'>»</span></span></li>"); } else { pagination.Append(String.Format("<li><a href='{0}'><span><span aria-hidden='true'>»</span></span></a></li>", BuildUrl(1))); } lPagination.Text = pagination.ToString(); } } }