public override void SetSubjectOperator(ResultOperatorBase resultOperator)
        {
            base.SetSubjectOperator(resultOperator);

            if (SubjectVariable != null)
            {
                if (resultOperator is CountResultOperator)
                {
                    var aggregate = new CountDistinctAggregate(new VariableTerm(SubjectVariable.Name));
                    SetSubjectVariable(aggregate.AsSparqlVariable(), true);
                }
                else if (resultOperator is FirstResultOperator)
                {
                    if (!IsRoot)
                    {
                        // Note: We currently only support First operators on root queries.
                        throw new NotSupportedException();
                    }
                }
                else
                {
                    throw new NotImplementedException(resultOperator.ToString());
                }
            }
        }
Esempio n. 2
0
        internal static List <Entity> ProcessAggregateFetchXml(XrmFakedContext ctx, XDocument xmlDoc, List <Entity> resultOfQuery)
        {
            // Validate that <all-attributes> is not present,
            // that all attributes have groupby or aggregate, and an alias,
            // and that there is exactly 1 groupby.
            if (RetrieveFetchXmlNode(xmlDoc, "all-attributes") != null)
            {
                throw new Exception("Can't have <all-attributes /> present when using aggregate");
            }

            var ns = xmlDoc.Root.Name.Namespace;

            var entityName = RetrieveFetchXmlNode(xmlDoc, "entity")?.GetAttribute("name")?.Value;

            if (string.IsNullOrEmpty(entityName))
            {
                throw new Exception("Can't find entity name for aggregate query");
            }

            var aggregates = new List <FetchAggregate>();
            var groups     = new List <FetchGrouping>();

            foreach (var attr in xmlDoc.Descendants(ns + "attribute"))
            {
                //TODO: Find entity alias. Handle aliasedvalue in the query result.

                var alias       = attr.GetAttribute("alias")?.Value;
                var logicalName = attr.GetAttribute("name")?.Value;
                if (string.IsNullOrEmpty("alias"))
                {
                    throw new Exception("Missing alias for attribute in aggregate fetch xml");
                }
                if (string.IsNullOrEmpty("name"))
                {
                    throw new Exception("Missing name for attribute in aggregate fetch xml");
                }

                if (attr.IsAttributeTrue("groupby"))
                {
                    var dategrouping = attr.GetAttribute("dategrouping")?.Value;
                    if (dategrouping != null)
                    {
                        DateGroupType t;
                        if (!Enum.TryParse(dategrouping, true, out t))
                        {
                            throw new Exception("Unknown dategrouping value '" + dategrouping + "'");
                        }
                        groups.Add(new DateTimeGroup()
                        {
                            Type        = t,
                            OutputAlias = alias,
                            Attribute   = logicalName
                        });
                    }
                    else
                    {
                        groups.Add(new SimpleValueGroup()
                        {
                            OutputAlias = alias,
                            Attribute   = logicalName
                        });
                    }
                }
                else
                {
                    var agrFn = attr.GetAttribute("aggregate")?.Value;
                    if (string.IsNullOrEmpty(agrFn))
                    {
                        throw new Exception("Attributes must have be aggregated or grouped by when using aggregation");
                    }

                    FetchAggregate newAgr = null;
                    switch (agrFn?.ToLower())
                    {
                    case "count":
                        newAgr = new CountAggregate();
                        break;

                    case "countcolumn":
                        if (attr.IsAttributeTrue("distinct"))
                        {
                            newAgr = new CountDistinctAggregate();
                        }
                        else
                        {
                            newAgr = new CountColumnAggregate();
                        }
                        break;

                    case "min":
                        newAgr = new MinAggregate();
                        break;

                    case "max":
                        newAgr = new MaxAggregate();
                        break;

                    case "avg":
                        newAgr = new AvgAggregate();
                        break;

                    case "sum":
                        newAgr = new SumAggregate();
                        break;

                    default:
                        throw new Exception("Unknown aggregate function '" + agrFn + "'");
                    }

                    newAgr.OutputAlias = alias;
                    newAgr.Attribute   = logicalName;
                    aggregates.Add(newAgr);
                }
            }

            List <Entity> aggregateResult;

            if (groups.Any())
            {
                aggregateResult = ProcessGroupedAggregate(entityName, resultOfQuery, aggregates, groups);
            }
            else
            {
                aggregateResult = new List <Entity>();
                var ent = ProcessAggregatesForSingleGroup(entityName, resultOfQuery, aggregates);
                aggregateResult.Add(ent);
            }

            return(OrderAggregateResult(xmlDoc, aggregateResult.AsQueryable()));
        }
        public override void SetObjectOperator(ResultOperatorBase resultOperator)
        {
            base.SetObjectOperator(resultOperator);

            if (ObjectVariable != null)
            {
                if (resultOperator is AnyResultOperator)
                {
                    // When using x.Any(), we add a LIMIT 1 the query results in the SparqlQueryGenerator.

                    // When using .Any(x => ..), the variable x is locally scoped and cannot be
                    // used in outer queries. Therefore do not need to actually select it.

                    // This avoids issues with Stardog:
                    // https://community.stardog.com/t/sparql-union-only-working-with-inferencing-enabled/1040/9
                }
                else if (resultOperator is AverageResultOperator)
                {
                    var aggregate = new AverageAggregate(new VariableTerm(ObjectVariable.Name));
                    SetObjectVariable(aggregate.AsSparqlVariable(), true);
                }
                else if (resultOperator is CountResultOperator)
                {
                    var aggregate = new CountDistinctAggregate(new VariableTerm(ObjectVariable.Name));
                    SetObjectVariable(aggregate.AsSparqlVariable(), true);
                }
                else if (resultOperator is FirstResultOperator)
                {
                    // "Using LIMIT and OFFSET to select different subsets of the query solutions
                    // will not be useful unless the order is made predictable by using ORDER BY."
                    // Source: https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#modOffset

                    // Therefore, if no ordering exists we add an ordering on the current subject
                    // to make the query result predictable.
                    if (!QueryModel.BodyClauses.OfType <OrderByClause>().Any())
                    {
                        OrderBy(SubjectVariable);
                    }
                    else
                    {
                        // In case the order was make explicit, we have to do nothing.
                    }

                    Limit(1);
                }
                else if (resultOperator is LastResultOperator)
                {
                    // "Using LIMIT and OFFSET to select different subsets of the query solutions
                    // will not be useful unless the order is made predictable by using ORDER BY."
                    // Source: https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#modOffset

                    // Therefore, if no ordering exists we add an ordering on the current subject
                    // to make the query result predictable.
                    if (!QueryModel.BodyClauses.OfType <OrderByClause>().Any())
                    {
                        OrderByDescending(SubjectVariable);
                    }
                    else
                    {
                        // Inverting the direction of the first ordering is handled in SparqlQueryModelVisitor.VisitOrdering().
                        // This is because the orderings are not necessarily processed *before* the result operators..
                    }

                    Limit(1);
                }
                else if (resultOperator is MaxResultOperator)
                {
                    var aggregate = new MaxAggregate(new VariableTerm(ObjectVariable.Name));
                    SetObjectVariable(aggregate.AsSparqlVariable(), true);
                }
                else if (resultOperator is MinResultOperator)
                {
                    var aggregate = new MinAggregate(new VariableTerm(ObjectVariable.Name));
                    SetObjectVariable(aggregate.AsSparqlVariable(), true);
                }
                else if (resultOperator is SumResultOperator)
                {
                    var aggregate = new SumAggregate(new VariableTerm(ObjectVariable.Name));
                    SetObjectVariable(aggregate.AsSparqlVariable(), true);
                }
                else if (resultOperator is OfTypeResultOperator)
                {
                    OfTypeResultOperator ofType = resultOperator as OfTypeResultOperator;
                    RdfClassAttribute    type   = ofType.SearchedItemType.TryGetCustomAttribute <RdfClassAttribute>();

                    if (type == null)
                    {
                        throw new ArgumentException("No RdfClass attrribute declared on type: " + ofType.SearchedItemType);
                    }

                    SparqlVariable s = ObjectVariable;
                    SparqlVariable p = VariableGenerator.CreatePredicateVariable();
                    SparqlVariable o = VariableGenerator.CreateObjectVariable();

                    WhereResource(s, p, o);
                    WhereResourceOfType(o, ofType.SearchedItemType);
                }
                else if (resultOperator is SkipResultOperator)
                {
                    SkipResultOperator op = resultOperator as SkipResultOperator;
                    Offset(int.Parse(op.Count.ToString()));
                }
                else if (resultOperator is TakeResultOperator)
                {
                    TakeResultOperator op = resultOperator as TakeResultOperator;
                    Limit(int.Parse(op.Count.ToString()));
                }
                else
                {
                    throw new NotImplementedException(resultOperator.ToString());
                }
            }
        }