internal override void PropagateDateTypesForOutSchema() { // projection may alter the schema with calculated columns // we calculate the data type of all the fields in the output schema // using type evaluation method on the QueryExpression // Map from out_alias to { projection_expression, // e.g.: (a.b + c) AS d // "d" -> (<expression of a.b+c>, <d's field in OutputSchema>) var exprToOutputMap = ProjectionMap.ToDictionary( kv => kv.Key, // key is output alias kv => new { Expr = kv.Value, Field = OutputSchema.First(f => f.FieldAlias == kv.Key) } // value is the corresponding field object and expression ); foreach (var map in exprToOutputMap) { // toggle the fact if any of the output column requires aggregation HasAggregationField = HasAggregationField || (map.Value.Expr.GetChildrenQueryExpressionType <QueryExpressionAggregationFunction>().Count() > 0); var allPropertyReferences = map.Value.Expr.GetChildrenQueryExpressionType <QueryExpressionProperty>(); // update types for all QueryExpressionPropty object first based on InputSchema (so QueryExpression.EvaluteType() will work) foreach (var prop in allPropertyReferences) { UpdatePropertyBasedOnAliasFromInputSchema(prop); } // then, update the type in the OutputSchema if (map.Value.Field is EntityField) { // This can only be direct exposure of entity (as opposed to deference of a particular property) // We just copy of the fields that the entity can potentially be dereferenced Debug.Assert(allPropertyReferences.Count() == 1); var varName = allPropertyReferences.First().VariableName; var matchInputField = InputSchema.First(f => f.FieldAlias == varName); map.Value.Field.Copy(matchInputField); } else { // This can be a complex expression involve multiple field/column references // We will compute the type of the expression Debug.Assert(map.Value.Field is ValueField); var evalutedType = map.Value.Expr.EvaluateType(); var outField = map.Value.Field as ValueField; outField.FieldType = evalutedType; } } }
/// <summary> /// This function bind the data source to a given graph definitions /// </summary> /// <param name="graphDefinition"></param> public void Bind(IGraphSchemaProvider graphDefinition) { // During binding, we read graph definition of the entity // and populate the EntityField object in the output // with the list of fields that the node/edge definition can expose var properties = new List <ValueField>(); string entityUniqueName; string sourceEntityName = null; string sinkEntityName = null; ValueField nodeIdField = null; ValueField edgeSrcIdField = null; ValueField edgeSinkIdField = null; try { if (Entity is NodeEntity) { NodeSchema nodeDef = graphDefinition.GetNodeDefinition(Entity.EntityName); entityUniqueName = nodeDef.Id; nodeIdField = new ValueField(nodeDef.NodeIdProperty.PropertyName, nodeDef.NodeIdProperty.DataType); properties.AddRange(nodeDef.Properties.Select(p => new ValueField(p.PropertyName, p.DataType))); properties.Add(nodeIdField); } else { var edgeEnt = Entity as RelationshipEntity; EdgeSchema edgeDef = null; switch (edgeEnt.RelationshipDirection) { case RelationshipEntity.Direction.Forward: edgeDef = graphDefinition.GetEdgeDefinition(edgeEnt.EntityName, edgeEnt.LeftEntityName, edgeEnt.RightEntityName); break; case RelationshipEntity.Direction.Backward: edgeDef = graphDefinition.GetEdgeDefinition(edgeEnt.EntityName, edgeEnt.RightEntityName, edgeEnt.LeftEntityName); break; default: // either direction // TODO: we don't handle 'both' direction yet Debug.Assert(edgeEnt.RelationshipDirection == RelationshipEntity.Direction.Both); edgeDef = graphDefinition.GetEdgeDefinition(edgeEnt.EntityName, edgeEnt.LeftEntityName, edgeEnt.RightEntityName); if (edgeDef == null) { edgeDef = graphDefinition.GetEdgeDefinition(edgeEnt.EntityName, edgeEnt.RightEntityName, edgeEnt.LeftEntityName); } break; } entityUniqueName = edgeDef.Id; sourceEntityName = edgeDef.SourceNodeId; sinkEntityName = edgeDef.SinkNodeId; edgeSrcIdField = new ValueField(edgeDef.SourceIdProperty.PropertyName, edgeDef.SourceIdProperty.DataType); edgeSinkIdField = new ValueField(edgeDef.SinkIdProperty.PropertyName, edgeDef.SinkIdProperty.DataType); properties.AddRange(edgeDef.Properties.Select(p => new ValueField(p.PropertyName, p.DataType))); properties.Add(edgeSrcIdField); properties.Add(edgeSinkIdField); } } catch (KeyNotFoundException e) { throw new TranspilerBindingException($"Failed to binding entity with alias '{Entity.Alias}' of type '{Entity.EntityName}' to graph definition. Inner error: {e.GetType().Name}: {e.Message}"); } Debug.Assert(OutputSchema.Count == 1 && OutputSchema.First() is EntityField && (OutputSchema.First() as EntityField).EntityName == Entity.EntityName); var field = OutputSchema.First() as EntityField; field.BoundEntityName = entityUniqueName; field.BoundSourceEntityName = sourceEntityName; field.BoundSinkEntityName = sinkEntityName; field.EncapsulatedFields = properties; field.NodeJoinField = nodeIdField; field.RelSourceJoinField = edgeSrcIdField; field.RelSinkJoinField = edgeSinkIdField; }
internal override void PropagateDateTypesForOutSchema() { // projection may alter the schema with calculated columns // we calculate the data type of all the fields in the output schema // using type evaluation method on the QueryExpression var exprToOutputMap = ProjectionMap.ToDictionary( kv => kv.Key, // key is output alias kv => new { Expr = kv.Value, Field = OutputSchema.First(f => f.FieldAlias == kv.Key) } // value is the corresponding field object and expression ); foreach (var map in exprToOutputMap) { // toggle the fact if any of the output column requires aggregation HasAggregationField = HasAggregationField || (map.Value.Expr.GetChildrenQueryExpressionType <QueryExpressionAggregationFunction>().Count() > 0); var allPropertyReferences = map.Value.Expr.GetChildrenQueryExpressionType <QueryExpressionProperty>(); if (map.Value.Field is EntityField) { // This can only be direct exposure of entity (as opposed to deference of a particular property) // We just copy of the fields that the entity can potentially be dereferenced Debug.Assert(allPropertyReferences.Count() == 1); var varName = allPropertyReferences.First().VariableName; var matchInputField = InputSchema.First(f => f.FieldAlias == varName); map.Value.Field.Copy(matchInputField); } else { // This can be a complex expression involve multiple field/column references // We will compute the type of the expression Debug.Assert(map.Value.Field is ValueField); // first of all, bind the type to the variable references foreach (var prop in allPropertyReferences) { var varName = prop.VariableName; var propName = prop.PropertyName; Debug.Assert(prop.VariableName != null); var matchedField = InputSchema.FirstOrDefault(f => f.FieldAlias == varName); if (matchedField == null) { throw new TranspilerBindingException($"Failed to find input matching field alias {varName}"); } if (string.IsNullOrEmpty(propName)) { // direct reference to an alias (value column or entity column) if (matchedField is ValueField) { prop.DataType = (matchedField as ValueField).FieldType; } else { // entity field reference in a single field expression // this is valid only in handful situations, such as Count(d), Count(distinct(d)) // in such case, we populate the Entity object with correct entity type so that code generator can use it later Debug.Assert(matchedField is EntityField); var matchedEntity = matchedField as EntityField; prop.Entity = matchedEntity.Type == EntityField.EntityType.Node ? new NodeEntity() { EntityName = matchedEntity.EntityName, Alias = matchedEntity.FieldAlias } as Entity: new RelationshipEntity() { EntityName = matchedEntity.EntityName, Alias = matchedEntity.FieldAlias } as Entity; } } else { // property dereference of an entity column if (!(matchedField is EntityField)) { throw new TranspilerBindingException($"Failed to dereference property {propName} for alias {varName}, which is not an alias of entity type as expected"); } var entField = matchedField as EntityField; var entPropField = entField.EncapsulatedFields.FirstOrDefault(f => f.FieldAlias == propName); if (entPropField == null) { throw new TranspilerBindingException($"Failed to dereference property {propName} for alias {varName}, Entity type {entField.BoundEntityName} does not have a property named {propName}"); } entField.AddReferenceFieldName(propName); prop.DataType = entPropField.FieldType; } } // do data type evaluation var evalutedType = map.Value.Expr.EvaluateType(); var outField = map.Value.Field as ValueField; outField.FieldType = evalutedType; } } }