/// <summary> /// Generate a new type for the group-by results and return an instance of the results with the aggregated values. /// </summary> /// <param name="groupByTrasformation">The group-by transformation clause.</param> /// <param name="keys">The collection of the group-by keys.</param> /// <param name="aggragatedValues">The results of the group-by aggregation.</param> /// <param name="keyType">The group-by key type.</param> /// <param name="context">The OData query context.</param> /// <returns>The group-by results as <see cref="IQueryable"/>.</returns> private IQueryable ProjectGroupedResult(ApplyGroupbyClause groupByTrasformation, IQueryable keys, object[] aggragatedValues, Type keyType, ODataQueryContext context) { List <object> result = new List <object>(); var keyProperties = keyType.GetProperties(); var projectionType = this.GetAggregationResultProjectionType(groupByTrasformation, keyType); int i = 0; foreach (var key in keys) { var objToProject = Activator.CreateInstance(projectionType); var pi = projectionType.GetProperty(groupByTrasformation.Aggregate.Alias); pi.SetValue(objToProject, aggragatedValues[i]); foreach (var key_pi in keyProperties) { if (key_pi.Name == "ComparerInstance") { continue; } pi = projectionType.GetProperty(key_pi.Name); pi.SetValue(objToProject, key_pi.GetValue(key)); } result.Add(objToProject); i++; } return(ExpressionHelpers.Cast(projectionType, result.AsQueryable())); }
/// <summary> /// Combine temporary results by simply writing them to a flat list of distinct items. /// </summary> /// <param name="temporaryResults">Results to combine.</param> /// <returns>A flatten list of all distinct temporary results.</returns> private IQueryable CombineTemporaryResults(List <Tuple <object, int> > temporaryResults) { if (!temporaryResults.Any()) { return(null); } Type elementType; if (temporaryResults.First().Item1 is IEnumerable <object> ) { elementType = (temporaryResults.First().Item1 as IEnumerable <object>).First().GetType(); } else { elementType = temporaryResults.First().Item1.GetType(); } var finalRes = new List <object>(); foreach (var item in temporaryResults.Select(pair => pair.Item1)) { if (item is IEnumerable <object> ) { finalRes.AddRange(item as IEnumerable <object>); } else { finalRes.Add(item); } } return(ExpressionHelpers.Cast(elementType, finalRes.AsQueryable())); }
/// <summary> /// Get the elements to memory. /// </summary> /// <typeparam name="T">The type of elements in the collection.</typeparam> /// <param name="index">The index in the original collection after enumeration.</param> /// <param name="limitReached">Was the max number of elements allowed to query in a single transaction reached.</param> /// <param name="dataToload">Data to bring to memory.</param> /// <returns>The IQueryable in memory.</returns> private IQueryable GetElements <T>(ref int index, out bool limitReached, object dataToload) { var realCollection = new List <T>(); var objectCollection = new List <object>(); if (dataToload is IEnumerable <T> ) { int counter = 0; limitReached = false; var enumerator = (dataToload as IEnumerable <T>).GetEnumerator(); while (enumerator.MoveNext()) { counter++; if (counter <= this.MaxCollectionSize) { realCollection.Add(enumerator.Current); index++; } else { limitReached = true; return(realCollection.AsQueryable()); } } return(realCollection.AsQueryable()); } else { if (dataToload is IQueryable) { int counter = 0; limitReached = false; var enumerator = (dataToload as IQueryable).GetEnumerator(); while (enumerator.MoveNext()) { counter++; if (counter <= this.MaxCollectionSize) { objectCollection.Add(enumerator.Current); index++; } else { limitReached = true; return(ExpressionHelpers.Cast((dataToload as IQueryable).ElementType, objectCollection.AsQueryable())); } } return(ExpressionHelpers.Cast((dataToload as IQueryable).ElementType, objectCollection.AsQueryable())); } throw new InvalidOperationException("cannot enumerate data"); } }
/// <summary> /// Generate a new type for the aggregated results and return an instance of the results with the aggregated value. /// </summary> /// <param name="dataToProject">The results to project.</param> /// <param name="alias">The name of the alias to use in the new type.</param> /// <param name="aliasType">The type of the alias to use in the new type.</param> /// <returns>An instance of the results with the aggregated value as <see cref="IQueryable"/>.</returns> private IQueryable ProjectResult(object dataToProject, string alias, Type aliasType) { var properties = new List <Tuple <Type, string> >() { new Tuple <Type, string>(aliasType, alias) }; var resType = AggregationTypesGenerator.CreateType(properties.Distinct(new TypeStringTupleComapere()).ToList(), Context, true); var objToProject = Activator.CreateInstance(resType); var pi = resType.GetProperty(alias); pi.SetValue(objToProject, dataToProject); var dataToProjectList = (new List <object>() { objToProject }).AsQueryable(); return(ExpressionHelpers.Cast(resType, dataToProjectList)); }
/// <summary> /// Implement the Count-Distinct aggregation method /// </summary> /// <param name="elementType">The type of entities</param> /// <param name="query">The collection</param> /// <param name="transformation">The transformation clause created by the parser</param> /// <param name="propertyToAggregateExpression">Projection Expression that defines access to the property to aggregate</param> /// <param name="parameters">A list of string parameters sent to the aggregation method</param> /// <returns>The Sum result</returns> public override object DoAggregatinon(Type elementType, IQueryable collection, ApplyAggregateClause transformation, LambdaExpression propertyToAggregateExpression, params string[] parameters) { var propertyType = GetAggregatedPropertyType(elementType, transformation.AggregatableProperty); var selectedValues = GetSelectedValues(elementType, collection, transformation, propertyToAggregateExpression); //call: (selected.AsQueryable() as IQueryable<double>).Ditinct(); var distinct = ExpressionHelpers.Distinct(propertyType, selectedValues); try { //call: (distinct.AsQueryable() as IQueryable<double>).Count(); return(ExpressionHelpers.Count(propertyType, distinct)); } catch (TargetInvocationException) { //salve a problem in mongo that throw the error "No further operators may follow Distinct in a LINQ query." when trying to construct the expression tree. distinct = ExpressionHelpers.Cast(propertyType, distinct.AllElements().AsQueryable()); return(ExpressionHelpers.Count(propertyType, distinct)); } }
/// <summary> /// Execute group-by with aggregation on the results. /// </summary> /// <param name="query">The collection to group.</param> /// <param name="maxResults">The max number of elements in a result set.</param> /// <param name="transformation">The group-by transformation as <see cref="ApplyGroupbyClause"/>.</param> /// <param name="keyType">The key type to group by.</param> /// <param name="propertiesToGroupByExpressions">Lambda expression that represents access to the properties to group by.</param> /// <param name="propertyToAggregateExpression">Lambda expression that represents access to the property to aggregate.</param> /// <param name="keys">Output the collection keys of the grouped results.</param> /// <param name="aggragatedValues">Output the aggregated results.</param> public void DoAggregatedGroupBy( IQueryable query, int maxResults, ApplyGroupbyClause transformation, Type keyType, IEnumerable <LambdaExpression> propertiesToGroupByExpressions, LambdaExpression propertyToAggregateExpression, out IQueryable keys, out object[] aggragatedValues) { var propToGroupBy = (propertiesToGroupByExpressions != null) ? propertiesToGroupByExpressions.ToArray() : null; var keySelector = this.GetGroupByProjectionLambda(transformation.SelectedStatements.ToArray(), keyType, propToGroupBy); object comparer = keyType.GetProperty("ComparerInstance").GetValue(null); var groupingResults = ExpressionHelpers.GroupBy(query, keySelector, this.Context.ElementClrType, keyType, comparer); var aggregationImplementation = AggregationMethodsImplementations.GetAggregationImplementation(transformation.Aggregate.AggregationMethod); // if group by is not supported in this IQueriable provider convert the grouping into memory implementation object convertedResult = null; int numberOfTempResults; if (QueriableProviderAdapter.ConvertionIsRequiredAsExpressionIfNotSupported(groupingResults, maxResults, out convertedResult, out numberOfTempResults)) { groupingResults = convertedResult as IQueryable; } var resType = typeof(List <>).MakeGenericType(this.Context.ElementClrType); keys = this.GetGroupingKeys(groupingResults, keyType, this.Context.ElementClrType); var groupedValues = this.GetGroupingValues(groupingResults, keyType, resType, this.Context.ElementClrType); // In case of paging due to memory execution of unsupported functions keys may not be distinct. // Here we make sure that keys are distinct and all values that belong to a key are written to the right list. List <object> distictKeys; List <object> groupedValuesPerKey; if (numberOfTempResults > 1) { this.CombineValuesListsPerKey(keys.AllElements(), groupedValues.AllElements(), out distictKeys, out groupedValuesPerKey); keys = distictKeys.AsQueryable(); } groupedValuesPerKey = groupedValues.AllElements(); int numberOfResults = groupedValuesPerKey.Count; var tmpAggragatedValues = new object[numberOfResults]; var projectionLambda = AggregationImplementationBase.GetProjectionLambda(this.Context.ElementClrType, transformation.Aggregate, propertyToAggregateExpression); string[] aggregationParams = AggregationImplementationBase.GetAggregationParams(transformation.Aggregate.AggregationMethod); Parallel.For(0, numberOfResults, (i => { IQueryable valuesAsQueryable; if (groupedValuesPerKey[i] is IEnumerable <object> ) { valuesAsQueryable = ExpressionHelpers.Cast(this.Context.ElementClrType, (groupedValuesPerKey[i] as IEnumerable <Object>).AsQueryable()); } else { valuesAsQueryable = ExpressionHelpers.Cast(this.Context.ElementClrType, (new List <Object>() { groupedValuesPerKey[i] }).AsQueryable()); } IQueryable queryToUse = valuesAsQueryable; if (transformation.Aggregate.AggregatableProperty.Contains('/')) { queryToUse = AggregationImplementationBase.FilterNullValues(query, this.Context.ElementClrType, transformation.Aggregate); } var aggragationResult = aggregationImplementation.DoAggregatinon(this.Context.ElementClrType, queryToUse, transformation.Aggregate, projectionLambda, aggregationParams); tmpAggragatedValues[i] = aggragationResult; })); aggragatedValues = tmpAggragatedValues; }