/// <summary> /// Performs a search using the specified <paramref name="sortOptions"/>. /// </summary> /// <param name="operation">The boolean operation the search should be based on.</param> /// <param name="sortOptions">The sort options specyfing how the results should be sorted.</param> /// <param name="results">The results of the search.</param> /// <param name="total">The total amount of results returned by the search.</param> protected virtual void Execute(IBooleanOperation operation, ISortOptions sortOptions, out IEnumerable <ISearchResult> results, out long total) { // Cast the boolean operation to IQueryExecutor IQueryExecutor executor = operation; // If "SortField" doesn't specified, we don't apply any sorting if (!string.IsNullOrWhiteSpace(sortOptions.SortField)) { switch (sortOptions.SortOrder) { case SortOrder.Ascending: executor = operation.OrderBy(new SortableField(sortOptions.SortField, sortOptions.SortType)); break; case SortOrder.Descending: executor = operation.OrderByDescending(new SortableField(sortOptions.SortField, sortOptions.SortType)); break; default: throw new Exception($"Unsupported sort order: {sortOptions.SortOrder}"); } } if (sortOptions is IOffsetOptions offset) { ISearchResults allResults = executor.Execute(); // Update the total amount of results total = allResults.TotalItemCount; // Apply limit and offset results = allResults .Skip(offset.Offset) .Take(offset.Limit); } else { // Since no offset or limit is specified, we request all results ISearchResults allResults = executor.Execute(int.MaxValue); // Update the total amount of results total = allResults.TotalItemCount; // Update "results" with the value of "allResults" as we're not filtering the results results = allResults; } }
private IEnumerable <ISearchResult> PerformExamineSearch(long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, string memberTypeAlias, IEnumerable <int> groups, string filter, IDictionary <string, string> additionalFilters = null, bool?isApproved = null, bool?isLockedOut = null) { if (!(InitialiseMemberQuery() is IQuery query)) { totalRecords = 0; return(Enumerable.Empty <ISearchResult>()); } IBooleanOperation op = null; if (!memberTypeAlias.IsNullOrWhiteSpace()) { op = query.NodeTypeAlias(memberTypeAlias); } if (groups?.Any() ?? false) { // Get group names from ids. var groupNames = memberGroupService.GetByIds(groups).Select(x => x.Name); if (groupNames.Any()) { // Keywords need to be all lowercase for Examine 2.0 op = query.And(op).GroupedOr(new[] { Constants.Members.Groups }, groupNames.Select(g => new ExamineValue(Examineness.Escaped, g.ToLower())).Cast <IExamineValue>().ToArray()); } } if (isApproved.HasValue) { op = query.And(op).BooleanField(Conventions.Member.IsApproved, isApproved.Value); } if (isLockedOut.HasValue) { op = query.And(op).BooleanField(Conventions.Member.IsLockedOut, isLockedOut.Value); } var basicFields = new List <string>() { "id", "__NodeId", "__Key", "email", "loginName" }; var filterParameters = additionalFilters.Where(q => q.Key.StartsWith("f_") && !string.IsNullOrWhiteSpace(q.Value)); //build a lucene query if (op == null && string.IsNullOrWhiteSpace(filter) && !filterParameters.Any()) { // Generic get everything (theoretically we shouldn't even get here)... op = query.NativeQuery("a* b* c* d* e* f* g* h* i* j* k* l* m* n* o* p* q* r* s* t* u* v* w* x* y* z*"); } else { if (!filter.IsNullOrWhiteSpace()) { // the __nodeName will be boosted 10x without wildcards // then __nodeName will be matched normally with wildcards // the rest will be normal without wildcards if (!string.IsNullOrWhiteSpace(filter)) { var sb = new StringBuilder(); sb.Append("+("); //node name exactly boost x 10 sb.AppendFormat("__nodeName:{0}^10.0 ", filter.ToLower()); //node name normally with wildcards sb.AppendFormat(" __nodeName:{0}* ", filter.ToLower()); foreach (var field in basicFields) { //additional fields normally sb.AppendFormat("{0}:{1} ", field, filter); } sb.Append(")"); op = query.And(op).NativeQuery(sb.ToString()); } } // Now specific field searching. - these should be ANDed and grouped. foreach (var qs in filterParameters) { string alias = qs.Key; if (alias.StartsWith("f_")) { alias = qs.Key.Substring(2); } var values = qs.Value.Split(','); if (values.Length > 0) { op = query.And(op).GroupedOr(new[] { alias }, values); } } } //// Order the results // Examine Sorting seems too unreliable, particularly on nodeName IOrdering ordering; if (orderDirection == Direction.Ascending) { ordering = op.OrderBy(new SortableField(orderBy.ToLower() == "name" ? "nodeName" : orderBy, SortType.String)); } else { ordering = op.OrderByDescending(new SortableField(orderBy.ToLower() == "name" ? "nodeName" : orderBy, SortType.String)); } #if NET5_0_OR_GREATER QueryOptions options = new(0, (int)(pageSize * (pageIndex + 1))); var results = ordering.Execute(options); #else var results = ordering.Execute((int)(pageSize * (pageIndex + 1))); #endif totalRecords = results.TotalItemCount; if (pageSize > 0) { int skipCount = (pageIndex > 0 && pageSize > 0) ? Convert.ToInt32((pageIndex - 1) * pageSize) : 0; if (totalRecords < skipCount) { skipCount = (int)totalRecords / pageSize; } return(results.Skip(skipCount).Take(pageSize)); } return(results); }