public async Task BuildAsync <T>(QueryBuilderContext <T> ctx) where T : class, new() { string filter = ctx.Source.GetFilterExpression() ?? String.Empty; if (String.IsNullOrEmpty(filter) && !ctx.Source.ShouldEnforceEventStackFilter()) { return; } // TODO: Handle search expressions as well bool altInvertRequested = false; if (filter.StartsWith("@!")) { altInvertRequested = true; filter = filter.Substring(2); ctx.Source.FilterExpression(filter); } var stackFilter = await EventStackFilterQueryVisitor.RunAsync(filter, EventStackFilterQueryMode.Stacks, ctx); var invertedStackFilter = await EventStackFilterQueryVisitor.RunAsync(filter, EventStackFilterQueryMode.InvertedStacks, ctx); if (ctx.Options.GetSoftDeleteMode() == SoftDeleteQueryMode.ActiveOnly) { invertedStackFilter.Query = !String.IsNullOrEmpty(invertedStackFilter.Query) ? $"(is_deleted:true OR ({invertedStackFilter.Query}))" : "is_deleted:true"; } // queries are the same, no need to allow inverting if (invertedStackFilter.Query == stackFilter.Query) { invertedStackFilter.IsInvertSuccessful = false; } const int stackIdLimit = 10000; string[] stackIds = null; string query = stackFilter.Query; bool isStackIdsNegated = stackFilter.HasStatusOpen && invertedStackFilter.IsInvertSuccessful && !altInvertRequested; if (isStackIdsNegated) { query = invertedStackFilter.Query; } if (String.IsNullOrEmpty(query) && (!ctx.Source.ShouldEnforceEventStackFilter() || ctx.Options.GetSoftDeleteMode() != SoftDeleteQueryMode.ActiveOnly)) { return; } _logger.LogTrace("Stack filter: {StackFilter} Invert Success: {InvertSuccess} Inverted: {InvertedStackFilter}", stackFilter.Query, invertedStackFilter.IsInvertSuccessful, invertedStackFilter.Query); if (!(ctx is IQueryVisitorContextWithValidator)) { var systemFilterQuery = GetSystemFilterQuery(ctx); systemFilterQuery.FilterExpression(query); var softDeleteMode = isStackIdsNegated ? SoftDeleteQueryMode.All : SoftDeleteQueryMode.ActiveOnly; var results = await _stackRepository.GetIdsByQueryAsync(q => systemFilterQuery.As <Stack>(), o => o.PageLimit(stackIdLimit).SoftDeleteMode(softDeleteMode)).AnyContext(); if (results.Total > stackIdLimit && (isStackIdsNegated || invertedStackFilter.IsInvertSuccessful)) { isStackIdsNegated = !isStackIdsNegated; query = isStackIdsNegated ? invertedStackFilter.Query : stackFilter.Query; systemFilterQuery.FilterExpression(query); softDeleteMode = isStackIdsNegated ? SoftDeleteQueryMode.All : SoftDeleteQueryMode.ActiveOnly; results = await _stackRepository.GetIdsByQueryAsync(q => systemFilterQuery.As <Stack>(), o => o.PageLimit(stackIdLimit).SoftDeleteMode(softDeleteMode)).AnyContext(); } if (results.Total > stackIdLimit) { throw new DocumentLimitExceededException("Please limit your search criteria."); } stackIds = results.Hits.Select(h => h.Id).ToArray(); } _logger.LogTrace("Setting stack filter with {IdCount} ids", stackIds?.Length ?? 0); if (!isStackIdsNegated) { if (stackIds.Length > 0) { ctx.Source.Stack(stackIds); } else { ctx.Source.Stack("none"); } } else { if (stackIds.Length > 0) { ctx.Source.ExcludeStack(stackIds); } } var eventsResult = await EventStackFilterQueryVisitor.RunAsync(filter, EventStackFilterQueryMode.Events, ctx); ctx.Source.FilterExpression(eventsResult.Query); }