QueryContainer DoFolderFiltering(QueryContainerDescriptor <Document> q, API.Common.Models.SearchRequest searchRequest) { var filters = new List <Func <QueryContainerDescriptor <Document>, QueryContainer> > { { f => f.Term(d => d.OrganizationKey, searchRequest.OrganizationIdentifier.OrganizationKey) } }; if (searchRequest.FolderIdentifier?.FolderKey != null) { filters.Add(f => f.Term(d => d.FolderKey, searchRequest.FolderIdentifier.FolderKey)); } return(q.Bool(b => b .Filter(filters.ToArray()) )); }
IEnumerable <object> UserQuery(API.Common.Models.SearchRequest searchRequest) { if (searchRequest.KeywordQuery != null) { yield return(new { wildcard = new { name = new { boost = 1.1, value = $"*{searchRequest.KeywordQuery}*" } } }); yield return(new { match = new { content = new { query = searchRequest.KeywordQuery } } }); } if (searchRequest.NativeQuery != null) { yield return new { query_string = new { query = searchRequest.NativeQuery } } } ; } async Task <ISearchResults> OldSearch( string[] securityIdentifiers, API.Common.Models.SearchRequest searchRequest) { Logger.LogInformation($"Search {searchRequest.FolderIdentifier} \"{searchRequest}\""); var identifiers = "_all " + string.Join(" ", securityIdentifiers ?? new string[0]); int FacetLimit = 10; SearchDescriptor <Document> searchDescriptor = null; var sortField = searchRequest.Paging.SortField; searchDescriptor = new SearchDescriptor <Document>() .Query(q => q .Bool(b => b .MinimumShouldMatch(1) .Filter( f => DoFolderFiltering(f, searchRequest), f => DoSecurity(f, securityIdentifiers), f => DoFacetFiltering(f, searchRequest.Filters) ) .Should(DoQueries(q, searchRequest)) ) ) .Highlight(h => h .PreTags("<em>") .PostTags("</em>") .Fields(fs => fs .Field(p => p.Content) .Type(HighlighterType.Fvh) .BoundaryMaxScan(50) .NumberOfFragments(3) ) ) .From(searchRequest.Paging.PageIndex * searchRequest.Paging.PageSize) .Size(searchRequest.Paging.PageSize == 0 ? 500 : searchRequest.Paging.PageSize) .Sort(ss => ss .Field(f => { f.Order(searchRequest.Paging.IsAscending ? SortOrder.Ascending : SortOrder.Descending); switch (searchRequest.Paging.SortField) { case "name": f.Field(ff => ff.Name); break; default: f.Field("_score"); f.Descending(); break; } return(f); }) ) .Aggregations(aggs => aggs .Nested("facet_strings", n => n .Path(p => p.FacetStrings) .Aggregations(aa => aa .Terms("facet_name", t => t .Field(new Field("facets.name")) .Aggregations(a2 => a2 .Terms("facet_value", t2 => t2 .Field(new Field("facets.value")) .Size(FacetLimit) ) ) ) ) ) ) ; string debugQuery = SerializeNESTObject(searchDescriptor); Logger.LogDebug($"Search Query: (index: {IndexName})\n {debugQuery}"); var results = await client.SearchAsync <Document>(searchDescriptor); //Logger.LogDebug($"Raw Results: {SerializeNESTObject(results)}"); var searchResults = new SearchResults { Rows = results.Hits.Select(d => new SearchResult { FileIdentifier = new FileIdentifier { OrganizationKey = d.Source.OrganizationKey, FolderKey = d.Source.FolderKey, FileKey = d.Source.FileKey }, Name = d.Source.Name, Extension = d.Source.Extension, MimeType = d.Source.MimeType, Metadata = d.Source.Metadata, Created = d.Source.Created, Modified = d.Source.Modified, Highlights = d.Highlights.SelectMany(h => h.Value.Highlights).ToArray(), Fields = d.Source.Fields }), TotalMatches = results.Total, DebugQuery = debugQuery }; var facetModels = new List <FacetModel>(); if ((results.Aggregations.FirstOrDefault(a => a.Key == "facet_strings") .Value as SingleBucketAggregate) ?.FirstOrDefault() .Value is BucketAggregate facets) { foreach (var facet in facets.Items.OfType <KeyedBucket <object> >()) { var model = new FacetModel { Name = facet.Key.ToString() }; var values = new List <FacetValue>(); model.Values = values; foreach (var container in facet.Values.OfType <BucketAggregate>()) { foreach (var facetValue in container.Items.OfType <KeyedBucket <object> >()) { values.Add(new FacetValue { Value = facetValue.Key.ToString(), Count = facetValue.DocCount }); } if (model.Values.Any() && container.SumOtherDocCount == 0) { if (model.Values.Count() == 1 && model.Values.First().Count == results.Total) { // this is a single facet describing all // results. it doesn't further differentiate the resultset. // skip it } else { facetModels.Add(model); } } } } } searchResults.Facets = facetModels; return(searchResults); }
private IEnumerable <object> NestedFacetFilters(API.Common.Models.SearchRequest searchRequest) { return(searchRequest?.Filters?.Select(f => NestedFacetFilter(f.Name, f.Value)) ?? new object[0]); }
async Task <ISearchResults> ISearch.Search( string[] securityIdentifiers, API.Common.Models.SearchRequest searchRequest) { var query = new { from = searchRequest.Paging.PageIndex * searchRequest.Paging.PageSize, size = searchRequest.Paging.PageSize, sort = new[] { new { _score = new { order = "desc" } } }, highlight = new { pre_tags = new[] { "<em>" }, post_tags = new[] { "</em>" }, fields = new { content = new { number_of_fragments = 3, boundary_max_scan = 50, type = "fvh" } } }, aggs = new { facet_strings = new { nested = new { path = "facets" }, aggs = new { facet_name = new { terms = new { field = "facets.name" }, aggs = new { facet_value = new { terms = new { field = "facets.value", size = 10 } } } } } } }, _source = new[] { "uniqueKey", "indexed", "organizationKey", "folderKey", "fileKey", "type", "name", "extension", "mimeType", "created", "modified", "mimeType", "fields", "metadata", "facets" }, query = new { @bool = new { should = UserQuery(searchRequest), filter = OrganizationAndFolderFilter(searchRequest.OrganizationIdentifier, searchRequest.FolderIdentifier) .Append(SecurityFilters(securityIdentifiers)) .Append(NestedFacetFilters(searchRequest)), minimum_should_match = 1 } } }; var queryJSON = JsonConvert.SerializeObject(query); var searchResponse = await lowLevelClient.SearchAsync <StringResponse>(IndexName, PostData.String(queryJSON)); var deserialized = JsonConvert.DeserializeObject <SearchResposne>(searchResponse.Body); var searchResults = new SearchResults { Rows = deserialized.Hits.Hits.Select(d => new SearchResult { FileIdentifier = new FileIdentifier { OrganizationKey = d._Source.OrganizationKey, FolderKey = d._Source.FolderKey, FileKey = d._Source.FileKey }, Name = d._Source.Name, Extension = d._Source.Extension, MimeType = d._Source.MimeType, Metadata = d._Source.Metadata, Created = d._Source.Created, Modified = d._Source.Modified, Highlights = d.Highlight?.SelectMany(h => h.Value).ToArray(), Fields = d._Source.Fields.Where(f => !String.IsNullOrWhiteSpace(f.Value)).ToDictionary(f => f.Key, f => f.Value) }), TotalMatches = deserialized.Hits.Total, DebugQuery = queryJSON }; var facetModels = new List <FacetModel>(); foreach (var facet in deserialized.Aggregations.Facet_Strings.Facet_Name.Buckets) { var model = new FacetModel { Name = facet.Key.ToString() }; var values = new List <FacetValue>(); model.Values = values; foreach (var facetValue in facet.Facet_Value.Buckets) { values.Add(new FacetValue { Value = facetValue.Key.ToString(), Count = facetValue.Doc_Count }); } if (model.Values.Any() && facet.Facet_Value.Sum_Other_Doc_Count == 0) { if (model.Values.Count() == 1 && model.Values.First().Count == deserialized.Hits.Total) { // this is a single facet describing all // results. it doesn't further differentiate the resultset. // skip it } else { facetModels.Add(model); } } } searchResults.Facets = facetModels; return(searchResults); }
Func <QueryContainerDescriptor <Document>, QueryContainer>[] DoQueries(QueryContainerDescriptor <Document> q, API.Common.Models.SearchRequest searchRequest) { var queries = new List <Func <QueryContainerDescriptor <Document>, QueryContainer> >(); if (searchRequest.KeywordQuery != null) { queries.Add(query => query .Wildcard(w => w .Boost(1.1) .Field(field => field.Name) .Value($"*{searchRequest.KeywordQuery}*") )); queries.Add(query => query .Match(m => m .Field(f => f.Content) .Query(searchRequest.KeywordQuery) )); } if (searchRequest.NativeQuery != null) { queries.Add(query => query .QueryString(qs => qs .Query(searchRequest.NativeQuery) ) ); } return(queries.ToArray()); }