Beispiel #1
0
 /// <summary>
 /// Creates a new instance of the <see cref="SearchParamTableExpression"/> class.
 /// </summary>
 /// <param name="queryGenerator">The search parameter query generator</param>
 /// <param name="predicate">The search expression over a columns belonging exclusively to a search parameter table.
 /// Applies to the chain target if a chained expression.</param>
 /// <param name="kind">The table expression kind.</param>
 /// <param name="chainLevel">The nesting chain nesting level of the current expression. 0 if not a chain expression.</param>
 public SearchParamTableExpression(
     SearchParamTableExpressionQueryGenerator queryGenerator,
     Expression predicate,
     SearchParamTableExpressionKind kind,
     int chainLevel = 0)
 {
     QueryGenerator = queryGenerator;
     Predicate      = predicate;
     Kind           = kind;
     ChainLevel     = chainLevel;
 }
        public override Expression VisitSqlRoot(SqlRootExpression expression, SqlSearchOptions context)
        {
            // If we only need the count, we don't want to execute any sort specific queries.
            if (context.CountOnly)
            {
                return(expression);
            }

            // Proceed if no sort params were requested.
            if (context.Sort.Count == 0)
            {
                return(expression);
            }

            // _type and _lastUpdated sort params are handled differently than others, because they can be
            // inferred directly from the resource table itself.
            if (context.Sort.All(s => s.searchParameterInfo.Name is SearchParameterNames.ResourceType or SearchParameterNames.LastUpdated))
            {
                return(expression);
            }

            // Check if the parameter being sorted on is also part of another parameter for the search.
            // If the parameter being sorted on is part of a filter then we don't need to run the seperate search for resources that are missing a value for the field being sorted on.
            // If the parameter being sorted on is not part of a filter we need to run a seperate search to get resources that don't have a value for the field being sorted on.
            bool matchFound = false;

            for (int i = 0; i < expression.SearchParamTableExpressions.Count; i++)
            {
                Expression updatedExpression = expression.SearchParamTableExpressions[i].Predicate.AcceptVisitor(this, context);
                if (updatedExpression == null)
                {
                    matchFound = true;
                    break;
                }
            }

            var newTableExpressions = new List <SearchParamTableExpression>();

            newTableExpressions.AddRange(expression.SearchParamTableExpressions);
            var continuationToken = ContinuationToken.FromString(context.ContinuationToken);

            if (!matchFound)
            {
                // We are running a sort query where the parameter by which we are sorting
                // is not present as part of other search parameters in the query.

                // Check whether we have to execute the second phase of the search for a sort query.
                // This can occur when SearchService decides to run a second search while processing the current query.
                // Or it could be a query from the client with a hardcoded "special" continuation token.
                if (context.SortQuerySecondPhase ||
                    (continuationToken != null &&
                     continuationToken.ResourceSurrogateId == 0 &&
                     continuationToken.SortValue == SqlSearchConstants.SortSentinelValueForCt))
                {
                    context.ContinuationToken = null;
                    if (context.Sort[0].sortOrder == SortOrder.Descending)
                    {
                        // For descending order, the second phase of the sort query deals with searching
                        // for resources that do not have a value for the _sort parameter.
                        var missingExpression   = Expression.MissingSearchParameter(context.Sort[0].searchParameterInfo, isMissing: true);
                        var queryGenForMissing  = _searchParamTableExpressionQueryGeneratorFactory.GetSearchParamTableExpressionQueryGenerator(context.Sort[0].searchParameterInfo);
                        var notExistsExpression = new SearchParamTableExpression(
                            queryGenForMissing,
                            missingExpression,
                            SearchParamTableExpressionKind.NotExists);

                        newTableExpressions.Add(notExistsExpression);

                        return(new SqlRootExpression(newTableExpressions, expression.ResourceTableExpressions));
                    }

                    // For ascending, the second phase of the sort query deals with searching
                    // for resources that have a value for the _sort parameter. So we will generate
                    // the appropriate Sort expression below.
                }
                else if (continuationToken != null && continuationToken.SortValue == null)
                {
                    // We have a ct for resourceid but not for the sort value.
                    // This means we are paging through resources that do not have values for the _sort parameter.
                    var missingExpression   = Expression.MissingSearchParameter(context.Sort[0].searchParameterInfo, isMissing: true);
                    var queryGenForMissing  = _searchParamTableExpressionQueryGeneratorFactory.GetSearchParamTableExpressionQueryGenerator(context.Sort[0].searchParameterInfo);
                    var notExistsExpression = new SearchParamTableExpression(
                        queryGenForMissing,
                        missingExpression,
                        SearchParamTableExpressionKind.NotExists);

                    newTableExpressions.Add(notExistsExpression);

                    return(new SqlRootExpression(newTableExpressions, expression.ResourceTableExpressions));
                }
                else if (continuationToken == null)
                {
                    // This means we are in the first "phase" of searching for resources for the _sort query.
                    // For ascending order, we will search for resources that do not have a value for the
                    // corresponding _sort parameter.
                    if (context.Sort[0].sortOrder == SortOrder.Ascending)
                    {
                        var missingExpression   = Expression.MissingSearchParameter(context.Sort[0].searchParameterInfo, isMissing: true);
                        var queryGenForMissing  = _searchParamTableExpressionQueryGeneratorFactory.GetSearchParamTableExpressionQueryGenerator(context.Sort[0].searchParameterInfo);
                        var notExistsExpression = new SearchParamTableExpression(
                            queryGenForMissing,
                            missingExpression,
                            SearchParamTableExpressionKind.NotExists);

                        newTableExpressions.Add(notExistsExpression);

                        return(new SqlRootExpression(newTableExpressions, expression.ResourceTableExpressions));
                    }

                    // For descending order, we will search for resources that have a value for the
                    // corresponding _sort parameter. We will generate the appropriate Sort expression below.
                }
            }

            SearchParamTableExpressionKind sortKind = matchFound ? SearchParamTableExpressionKind.SortWithFilter : SearchParamTableExpressionKind.Sort;

            if (sortKind == SearchParamTableExpressionKind.SortWithFilter)
            {
                context.IsSortWithFilter = true;
            }

            var queryGenerator = _searchParamTableExpressionQueryGeneratorFactory.GetSearchParamTableExpressionQueryGenerator(context.Sort[0].searchParameterInfo);

            newTableExpressions.Add(new SearchParamTableExpression(queryGenerator, new SortExpression(context.Sort[0].searchParameterInfo), sortKind));

            return(new SqlRootExpression(newTableExpressions, expression.ResourceTableExpressions));
        }