private int GetCount(IQueryOver <AggregateNodeStatus, AggregateNodeStatus> aggQuery, bool requiresLoadingIds) { if (requiresLoadingIds) { // We can't do a count in the db engine and must load the distinct Ids instead var ids = aggQuery .Select(Projections.Distinct(Projections.Property <AggregateNodeStatus>(x => x.Node.Id))) .Future <Guid>(); return(ids.Distinct().Count()); } int count; if (Helper.SupportsCountDistinct()) { // Set the aggregate to return a count of the distinct NodeIds aggQuery = aggQuery.Select(Projections.CountDistinct <AggregateNodeStatus>(x => x.Node.Id)); count = aggQuery.FutureValue <int>().Value; } else { // SqlCe doesn't support distinct in aggregates, so create a subquery // The ideal subquery for SqlCe would be // select count(*) from (select distinct NodeId ...) // However you can't select from a derived table in NH so we have to do a // select count(*) where exists (select distinct NodeId .. where versionid & statusid match) // We could revert back to the ideal version if we used manual Sql concatenation // Create the distinct NodeId projection aggQuery = aggQuery.Select(Projections.Distinct(Projections.Property <AggregateNodeStatus>(x => x.Node.Id))); // Add some criteria to the inner aggregate query to tie it to the new outer one we'll create for the count AggregateNodeStatus outer = null; aggQuery = aggQuery .Where(x => x.NodeVersion == outer.NodeVersion) .And(x => x.StatusType == outer.StatusType); var outerQuery = Helper.NhSession.QueryOver(() => outer) .Select(Projections.Count <AggregateNodeStatus>(x => x.Node.Id)) .WithSubquery.WhereExists((QueryOver <AggregateNodeStatus>)aggQuery); count = outerQuery.FutureValue <int>().Value; } return(count); }
private static void ConjoinAsAndOperation(ICriterion rightCriterion, BinaryFilterResult toReturn, AggregateNodeStatus agg, FilterResult left, ICriterion leftCriterion, FilterResult right, Join[] allJoins, QueryOver <AggregateNodeStatus, AggregateNodeStatus> newQuery) { // If either side is a binary, it'll already have a subquery generated, so can use that, // otherwise we need to make one var leftAsBinary = left as BinaryFilterResult; var rightAsBinary = right as BinaryFilterResult; var leftAsSchema = left as SchemaFilterResult; var rightAsSchema = right as SchemaFilterResult; var addedLeftSchema = false; var addedRightSchema = false; if (leftAsSchema != null && rightAsSchema != null) { toReturn.Joins = allJoins; var addSchemaJoin = Restrictions.Conjunction() .Add(leftAsSchema.NhCriterion) .Add(rightAsSchema.NhCriterion); toReturn.Subquery = newQuery.Where(addSchemaJoin); } else if (leftAsSchema != null) { toReturn.Joins = allJoins; var theQuery = QueryOver.Of(() => agg); if (rightCriterion != null) { theQuery = theQuery.Where(rightCriterion); } foreach (var rightJoin in allJoins) { theQuery = theQuery.JoinAlias(rightJoin.Path, rightJoin.Alias, rightJoin.JoinType); } // One side is a schema restriction, and we can generate faster sql by avoiding // a subquery and just adding the schema restriction to the field restriction(s) var addSchemaJoin = Restrictions.Conjunction() .Add(leftAsSchema.NhCriterion) .Add(GetAutoSubqueryCriterion(right)); theQuery = theQuery.Where(addSchemaJoin); toReturn.Subquery = theQuery; return; addedLeftSchema = true; } else if (rightAsSchema != null) { toReturn.Joins = allJoins; var theQuery = QueryOver.Of(() => agg); if (leftCriterion != null) { theQuery = theQuery.Where(rightCriterion); } foreach (var rightJoin in allJoins) { theQuery = theQuery.JoinAlias(rightJoin.Path, rightJoin.Alias, rightJoin.JoinType); } // One side is a schema restriction, and we can generate faster sql by avoiding // a subquery and just adding the schema restriction to the field restriction(s) var addSchemaJoin = Restrictions.Conjunction() .Add(GetAutoSubqueryCriterion(left)) .Add(rightAsSchema.NhCriterion); theQuery = theQuery.Where(addSchemaJoin); toReturn.Subquery = theQuery; return; addedRightSchema = true; } if (leftAsBinary == null && !addedLeftSchema) { left.Subquery = QueryOver.Of(() => agg); if (leftCriterion != null) { left.Subquery = left.Subquery.Where(leftCriterion); } foreach (var leftJoin in left.Joins) { left.Subquery = left.Subquery.JoinAlias(leftJoin.Path, leftJoin.Alias, leftJoin.JoinType); } } if (rightAsBinary == null && !addedRightSchema) { right.Subquery = QueryOver.Of(() => agg); if (rightCriterion != null) { right.Subquery = right.Subquery.Where(rightCriterion); } foreach (var rightJoin in right.Joins) { right.Subquery = right.Subquery.JoinAlias(rightJoin.Path, rightJoin.Alias, rightJoin.JoinType); } } toReturn.Joins = new List <Join>(left.Joins); toReturn.Subquery = left .Subquery .WithSubquery.WhereProperty(x => x.NodeVersion.Id).In(right.Subquery.Select(x => x.NodeVersion.Id)); }
public override FilterResult VisitBinary(BinaryExpression node) { var left = Visit(node.Left); var right = Visit(node.Right); if (left == null && right != null) { return(right); } if (left != null && right == null) { return(left); } AggregateNodeStatus agg = null; // Combine a distinct set of all the joins in the left and right sides, we might use this later var allJoins = left.Joins.Concat(right.Joins) .DistinctBy(x => x.Path.ToString() + x.Alias.ToString() + x.JoinType.ToString()).ToArray(); var newQuery = QueryOver.Of <AggregateNodeStatus>(() => agg); foreach (var allJoin in allJoins) { newQuery = newQuery.JoinAlias(allJoin.Path, allJoin.Alias, allJoin.JoinType); } // If we're returning a binary result, we need to let the upper branch in the tree know if this is // and And or an Or binary. If it's an And binary, then we're going to have subqueries, because in the EVM // datamodel you can't do "and" expressions on the same join because different values occupy different rows. // If we have subqueries, we need to be aware of that when combining the "outer" binary bearing in mind there // may be several compound binaries in the expression tree and thus this method might be recursively walking // the tree (depth-first of course) var isConjunction = false; switch (node.NodeType) { case ExpressionType.And: case ExpressionType.AndAlso: isConjunction = true; break; case ExpressionType.Or: case ExpressionType.OrElse: break; default: throw new InvalidOperationException("This provider only supports binary expressions with And, AndAlso, Or, OrElse expression types. ExpressionType was {0}".InvariantFormat(node.NodeType.ToString())); } var toReturn = new BinaryFilterResult(isConjunction); var leftCriterion = GetCriterion(left); var rightCriterion = GetCriterion(right); if (toReturn.IsAndOperation) { // To do an "and", we'll construct the left and right queries, and the output // query will be "where left also has a subquery matching right" ConjoinAsAndOperation(rightCriterion, toReturn, agg, left, leftCriterion, right, allJoins, newQuery); } else { // To do an "or", we can combine the queries into one, provided either side isn't a Binary that is an "and" // in which case we need to add it as a subquery as its simple criterion won't have enough info bool leftIsSubquery = false; bool rightIsSubquery = false; var leftAsBinary = left as BinaryFilterResult; var rightAsBinary = right as BinaryFilterResult; if (leftAsBinary != null && leftAsBinary.IsAndOperation) { leftIsSubquery = true; } if (rightAsBinary != null && rightAsBinary.IsAndOperation) { rightIsSubquery = true; } if (leftIsSubquery && rightIsSubquery) { ConjoinAsAndOperation(rightCriterion, toReturn, agg, left, leftCriterion, right, allJoins, newQuery); } else if (rightIsSubquery) { toReturn.Subquery = newQuery; var rightSubquery = Subqueries.WhereProperty <AggregateNodeStatus>(x => x.NodeVersion.Id).In(right.Subquery.Select(x => x.NodeVersion.Id)); if (leftCriterion != null) { var disjunction = Restrictions.Disjunction().Add(leftCriterion).Add(rightSubquery); toReturn.Subquery = toReturn.Subquery.Where(disjunction); toReturn.Joins = left.Joins; } else { toReturn.Subquery = toReturn.Subquery.Where(rightSubquery); } } else if (leftIsSubquery) { toReturn.Subquery = newQuery; var leftSubquery = Subqueries.WhereProperty <AggregateNodeStatus>(x => x.NodeVersion.Id).In(left.Subquery.Select(x => x.NodeVersion.Id)); if (rightCriterion != null) { var disjunction = Restrictions.Disjunction().Add(rightCriterion).Add(leftSubquery); toReturn.Subquery = toReturn.Subquery.Where(disjunction); toReturn.Joins = right.Joins; } else { toReturn.Subquery = toReturn.Subquery.Where(leftSubquery); } } else { var orBoth = Restrictions.Disjunction() .Add(leftCriterion) .Add(rightCriterion); newQuery = newQuery.Where(orBoth); toReturn.NhCriterion = orBoth; toReturn.Joins = new List <Join>(allJoins); toReturn.Subquery = newQuery; } } return(toReturn); }