public IActionResult Index(IFormCollection form) { ConnectionSettings connectionSettings = new ConnectionSettings(new Uri("http://10.1.9.180:9200")); connectionSettings.DefaultIndex("product_pool"); var mapping = this.client.Instance.GetMapping(new GetMappingRequest(Nest.Indices.Index("product_pool"))); List <KeyValuePair <string, string> > FieldList = mapping.Indices.First().Value.Mappings.Values.First().Properties.Select(x => new KeyValuePair <string, string>(x.Key.Name, x.Value.Type)) .ToList(); SearchDescriptor <Dictionary <string, object> > search = new SearchDescriptor <Dictionary <string, object> >(); search = search.Index("product_pool"); search = search.AllTypes(); var qr = new QueryContainerDescriptor <Dictionary <string, object> >(); QueryContainer queryContainer = new QueryContainer(); foreach (var itePair in form.Where(x => !string.IsNullOrEmpty(x.Value)).ToList()) { if (FieldList.Any(x => x.Key == itePair.Key.Replace("searchKey_", ""))) { queryContainer &= qr.Term(itePair.Key.Replace("searchKey_", ""), itePair.Value[0]); } } search.Query(descriptor => queryContainer); var documents = this.client.Instance.Search <Dictionary <string, object> >(search); //var documents = this.client.Instance.Search<Dictionary<string, object>>(new SearchRequest() { Query = queryContainer }); //var documents = this.client.Instance.Search<Dictionary<string, object>>(x => x.Index("product_pool").AllTypes().MatchAll()); var model = new SearchViewModel { Documents = documents.Hits.Select(x => x.Source).ToList(), FieldList = FieldList }; return(View(model)); }
/// <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); }
internal IQueryResponse <Dictionary <string, object> > Execute(ElasticSearchQuery query, Type resultType) { var index = _context.Index as ElasticSearchIndex; if (index == null) { return(new QueryResponse <Dictionary <string, object> >()); } var descriptor = new SearchDescriptor <Dictionary <string, object> >(); descriptor.Query(query.Query); //TODO: need to determine how to allow type specification, so that we don't have to hard-code AllTypes //NEST generates the search query URL based on the descriptor type, so if we use Dictionary<string, object> as the descriptor type, the query URL is: //http://[server]:9200/[indexname]/dictionary`2s/_search //instead we want it to be: //http://[server]:9200/[indexname]/_search //when searching over all types. so basically, we want the default to AllTypes unless a specific type is used. //In the context of Sitecore search, is it even possible to specify a type for the search? probably, but would need some sort of type mapping... descriptor.AllTypes(); if (query.Filter != null) { descriptor.Filter(filterDescriptor => filterDescriptor.Query(q => query.Filter)); } if (!Settings.DefaultLanguage.StartsWith(_cultureCode)) { descriptor.Filter(f => f.Query(q => q.Term("_language", _cultureCode))); } var isResultsSizeSet = false; if (query.Methods != null) { var fields = new List <string>(); var selectMethods = (from m in query.Methods where m.MethodType == QueryMethodType.Select select(SelectMethod) m).ToList <SelectMethod>(); if (selectMethods.Any()) { foreach (var method in selectMethods) { fields.AddRange(method.FieldNames.Select(fieldName => fieldName.ToLowerInvariant())); } if (!_context.SecurityOptions.HasFlag(SearchSecurityOptions.DisableSecurityCheck)) { fields.Add("_uniqueid"); fields.Add("_datasource"); } } var getResultsMethods = (from m in query.Methods where m.MethodType == QueryMethodType.GetResults select(GetResultsMethod) m).ToList <GetResultsMethod>(); if (getResultsMethods.Any()) { if (fields.Count > 0) { fields.Add("score"); } } if (fields.Count > 0) { descriptor.Fields(fields.ToArray()); } var orderByMethods = (from m in query.Methods where m.MethodType == QueryMethodType.OrderBy select(OrderByMethod) m).ToList <OrderByMethod>(); if (orderByMethods.Any()) { foreach (var method in orderByMethods) { var fieldName = method.Field; switch (method.SortDirection) { case SortDirection.Ascending: descriptor.SortAscending(fieldName); break; case SortDirection.Descending: descriptor.SortDescending(fieldName); break; } } } var skipMethods = (from m in query.Methods where m.MethodType == QueryMethodType.Skip select(SkipMethod) m).ToList <SkipMethod>(); if (skipMethods.Any()) { var num = skipMethods.Sum(skipMethod => skipMethod.Count); descriptor.Skip(num); } var takeMethods = (from m in query.Methods where m.MethodType == QueryMethodType.Take select(TakeMethod) m).ToList <TakeMethod>(); if (takeMethods.Any()) { var num2 = takeMethods.Sum(takeMethod => takeMethod.Count); descriptor.Size(num2); //Take is actually just an alias for Size in NEST, so just use Size instead. isResultsSizeSet = true; } var countMethods = (from m in query.Methods where m.MethodType == QueryMethodType.Count select(CountMethod) m).ToList <CountMethod>(); if (query.Methods.Count == 1 && countMethods.Any()) { descriptor.Size(0); //TODO: is using Size appropriate here? and is "0" the proper value to send? isResultsSizeSet = true; } var anyMethods = (from m in query.Methods where m.MethodType == QueryMethodType.Any select(AnyMethod) m).ToList <AnyMethod>(); if (query.Methods.Count == 1 && anyMethods.Any()) { descriptor.Size(0); //TODO: is using Size appropriate here? and is "0" the proper value to send? isResultsSizeSet = true; } var getFacetsMethods = (from m in query.Methods where m.MethodType == QueryMethodType.GetFacets select(GetFacetsMethod) m).ToList <GetFacetsMethod>(); //TODO: implement facet querying if ((query.FacetQueries.Count > 0) && (getFacetsMethods.Any() || getResultsMethods.Any())) { //foreach (var facetQuery in GetFacetsPipeline.Run(new GetFacetsArgs(null, query.FacetQueries, _context.Index.Configuration.VirtualFieldProcessors, _context.Index.FieldNameTranslator)).FacetQueries.ToHashSet()) //{ // if (!facetQuery.FieldNames.Any()) // continue; // var nullable = facetQuery.MinimumResultCount; // if (facetQuery.FieldNames.Count() == 1) // { // var fieldNameTranslator = FieldNameTranslator as ElasticSearchFieldNameTranslator; // var indexFieldName = facetQuery.FieldNames.First(); // if (((fieldNameTranslator != null) && (indexFieldName == fieldNameTranslator.StripKnownExtensions(indexFieldName))) && (_context.Index.Configuration.FieldMap.GetFieldConfiguration(indexFieldName) == null)) // { // indexFieldName = fieldNameTranslator.GetIndexFieldName(indexFieldName.Replace("__", "!").Replace("_", " ").Replace("!", "__"), true); // } // IElasticSearchFacetQuery[] queries = new IElasticSearchFacetQuery[1]; // ElasticSearchFacetFieldQuery query2 = new ElasticSearchFacetFieldQuery(indexFieldName) // { // MinCount = nullable // }; // queries[0] = query2; // options.AddFacets(queries); // } // if (facetQuery.FieldNames.Any()) // { // IElasticSearchFacetQuery[] queryArray2 = new IElasticSearchFacetQuery[1]; // ElasticSearchFacetPivotQuery query3 = new ElasticSearchFacetPivotQuery // { // Fields = new[] { string.Join(",", facetQuery.FieldNames) }, // MinCount = nullable // }; // queryArray2[0] = query3; // options.AddFacets(queryArray2); // } //} } } if (!isResultsSizeSet) { descriptor.Size(ContentSearchConfigurationSettings.SearchMaxResults); } //var blee = JsonConvert.SerializeObject(descriptor, Formatting.Indented); var serializedDescriptor = index.Client.Serialize(descriptor); SearchLog.Log.Info("Serialized Query - " + serializedDescriptor); var response = index.Client.Search(descriptor); if (!response.ConnectionStatus.Success) { SearchLog.Log.Error("Query exception - " + response.ConnectionStatus.Error.OriginalException); } return(response); }