/// We compile each entityQuery with EqlCompiler and build a Select call from the fields public override DataApiNode VisitEntityQuery(EqlGrammerParser.EntityQueryContext context) { string name; string query; if (context.alias != null) { name = context.alias.name.GetText(); query = context.entity.GetText(); } else { query = context.entity.GetText(); name = query; if (name.IndexOf(".") > -1) { name = name.Substring(0, name.IndexOf(".")); } } try { if (_selectContext == null) { // top level are queries on the context var exp = EqlCompiler.Compile(query, _schemaProvider, _methodProvider).Expression; var topLevelSelect = BuildDynamicSelectOnCollection(exp, name, context, true); return(topLevelSelect); } // other levels are object selection. e.g. from the top level people query I am selecting all their children { field1, etc. } return(BuildDynamicSelectForObjectGraph(query, name, context)); } catch (EqlCompilerException ex) { //return DataApiNode.MakeError(name, $"Error compiling field or query '{query}'. {ex.Message}"); throw DataApiException.MakeFieldCompileError(query, ex.Message); } }
/// <summary> /// Visit a parse tree produced by <see cref="EqlGrammerParser.entityQuery"/>. /// <para> /// The default implementation returns the result of calling <see cref="AbstractParseTreeVisitor{Result}.VisitChildren(IRuleNode)"/> /// on <paramref name="context"/>. /// </para> /// </summary> /// <param name="context">The parse tree.</param> /// <return>The visitor result.</return> public virtual Result VisitEntityQuery([NotNull] EqlGrammerParser.EntityQueryContext context) { return(VisitChildren(context)); }
/// Given a syntax of someField { fields, to, selection, from, object } /// it will figure out if 'someField' is an IEnumerable or an istance of the object (not a collection) and build the correct select statement private DataApiNode BuildDynamicSelectForObjectGraph(string query, string name, EqlGrammerParser.EntityQueryContext context) { if (!_schemaProvider.TypeHasField(_selectContext.Type.Name, name)) { throw new EqlCompilerException($"Type {_selectContext.Type} does not have field or property {name}"); } name = _schemaProvider.GetActualFieldName(_selectContext.Type.Name, name); // Don't really like any of this, but... try { var result = EqlCompiler.CompileWith(query, _selectContext, _schemaProvider, _methodProvider); var exp = result.Expression; if (exp.Body.Type.IsEnumerable()) { return(BuildDynamicSelectOnCollection(exp, name, context, false)); } var oldContext = _selectContext; _selectContext = exp.Body; // visit child fields. Will be field or entityQueries again var fieldExpressions = context.fields.children.Select(c => Visit(c)).Where(n => n != null).ToList(); var relationsExps = fieldExpressions.Where(f => f.Expression.NodeType == ExpressionType.MemberInit || f.Expression.NodeType == ExpressionType.Call).ToList(); if (_relationHandler != null && relationsExps.Any()) { var parameterExpression = Expression.Parameter(_selectContext.Type); var relations = relationsExps.Select(r => (Expression)Expression.PropertyOrField(parameterExpression, r.Name)).ToList(); exp = _relationHandler.BuildNodeForSelect(relations, parameterExpression, exp, name, _schemaProvider); } var newExp = DataApiExpressionUtil.CreateNewExpression(_selectContext, fieldExpressions, _schemaProvider); _selectContext = oldContext; return(new DataApiNode(_schemaProvider.GetActualFieldName(_selectContext.Type.Name, name), newExp, exp.Parameters.Any() ? exp.Parameters.First() : null, exp.Body)); } catch (EqlCompilerException ex) { throw DataApiException.MakeFieldCompileError(query, ex.Message); } }
/// Given a syntax of someCollection { fields, to, selection, from, object } /// it will build a select assuming 'someCollection' is an IEnumerable private DataApiNode BuildDynamicSelectOnCollection(LambdaExpression exp, string name, EqlGrammerParser.EntityQueryContext context, bool isRootSelect) { var elementType = exp.Body.Type.GetEnumerableType(); var contextParameter = Expression.Parameter(elementType); var oldContext = _selectContext; _selectContext = contextParameter; // visit child fields. Will be field or entityQueries again var fieldExpressions = context.fields.children.Select(c => Visit(c)).Where(n => n != null).ToList(); var relations = fieldExpressions.Where(f => f.Expression.NodeType == ExpressionType.MemberInit || f.Expression.NodeType == ExpressionType.Call).Select(r => r.RelationExpression).ToList(); // process at each relation if (_relationHandler != null && relations.Any()) { // Likely the EF handler to build .Include()s exp = _relationHandler.BuildNodeForSelect(relations, contextParameter, exp, name, _schemaProvider); } // we're about to add the .Select() call. May need to do something if (_relationHandler != null && isRootSelect) { exp = _relationHandler.HandleSelectComplete(exp); } // Default we select out sub objects/relations. So Select(d => new {Field = d.Field, Relation = new { d.Relation.Field }}) var selectExpression = DataApiExpressionUtil.SelectDynamic(contextParameter, exp.Body, fieldExpressions, _schemaProvider); var node = new DataApiNode(name, selectExpression, exp.Parameters.Any() ? exp.Parameters.First() : null, exp.Body); _selectContext = oldContext; return(node); }